diff options
313 files changed, 15007 insertions, 3714 deletions
diff --git a/.qmake.conf b/.qmake.conf index 3c71512ec..26852b80b 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,4 @@ QTWEBENGINE_OUT_ROOT = $$shadowed($$PWD) load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.12.2 +MODULE_VERSION = 5.13.0 diff --git a/config.tests/winversion/winversion.cpp b/config.tests/winversion/winversion.cpp index 4d117b945..e9a4cff59 100644 --- a/config.tests/winversion/winversion.cpp +++ b/config.tests/winversion/winversion.cpp @@ -26,7 +26,7 @@ ** ****************************************************************************/ -#if !defined(__clang__) && _MSC_FULL_VER < 191125507 +#if !defined(__clang__) && _MSC_FULL_VER < 191426428 #error unsupported Visual Studio version #endif diff --git a/configure.json b/configure.json index 40d6bfe56..60b7342f7 100644 --- a/configure.json +++ b/configure.json @@ -1,5 +1,6 @@ { "subconfigs": [ - "src/core" + "src/core", + "src/webengine" ] } diff --git a/configure.pri b/configure.pri index 26c57ce61..8db31ddd3 100644 --- a/configure.pri +++ b/configure.pri @@ -312,3 +312,8 @@ defineTest(qtConfTest_hasThumbFlag) { # no flag assume mthumb return(true) } + +defineTest(qtConfTest_hasGcc6OrNewer) { + greaterThan(QMAKE_GCC_MAJOR_VERSION, 5):return(true) + return(false) +} diff --git a/examples/webengine/quicknanobrowser/ApplicationRoot.qml b/examples/webengine/quicknanobrowser/ApplicationRoot.qml index 67b686541..3bc571546 100644 --- a/examples/webengine/quicknanobrowser/ApplicationRoot.qml +++ b/examples/webengine/quicknanobrowser/ApplicationRoot.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.1 -import QtWebEngine 1.2 +import QtWebEngine 1.9 QtObject { id: root @@ -57,6 +57,7 @@ QtObject { property QtObject defaultProfile: WebEngineProfile { storageName: "Profile" offTheRecord: false + useForGlobalCertificateVerification: true } property QtObject otrProfile: WebEngineProfile { diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index 0ac69ef24..d3d597e2b 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -57,7 +57,7 @@ import QtQuick.Controls.Styles 1.0 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.0 import QtQuick.Window 2.1 -import QtWebEngine 1.7 +import QtWebEngine 1.9 ApplicationWindow { id: browserWindow @@ -89,6 +89,7 @@ ApplicationWindow { property alias touchIconsEnabled: touchIconsEnabled.checked property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked property alias devToolsEnabled: devToolsEnabled.checked + property alias pdfViewerEnabled: pdfViewerEnabled.checked } Action { @@ -333,6 +334,12 @@ ApplicationWindow { checkable: true checked: false } + MenuItem { + id: pdfViewerEnabled + text: "PDF viewer enabled" + checkable: true + checked: WebEngine.settings.pdfViewerEnabled + } } } } @@ -411,6 +418,7 @@ ApplicationWindow { settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage settings.touchIconsEnabled: appSettings.touchIconsEnabled settings.webRTCPublicInterfacesOnly: appSettings.webRTCPublicInterfacesOnly + settings.pdfViewerEnabled: appSettings.pdfViewerEnabled onCertificateError: function(error) { error.defer(); @@ -491,6 +499,10 @@ ApplicationWindow { tabs.removeTab(tabs.currentIndex); } + onSelectClientCertificate: function(selection) { + selection.certificates[0].select(); + } + Timer { id: reloadTimer interval: 0 diff --git a/examples/webengine/webengine.pro b/examples/webengine/webengine.pro index b20b8f118..5ad620390 100644 --- a/examples/webengine/webengine.pro +++ b/examples/webengine/webengine.pro @@ -3,7 +3,8 @@ TEMPLATE=subdirs SUBDIRS += \ customdialogs \ minimal \ - quicknanobrowser + quicknanobrowser \ + webengineaction qtHaveModule(quickcontrols2) { SUBDIRS += \ diff --git a/examples/webengine/webengineaction/doc/images/webengineaction-example.png b/examples/webengine/webengineaction/doc/images/webengineaction-example.png Binary files differnew file mode 100644 index 000000000..2e34bbf63 --- /dev/null +++ b/examples/webengine/webengineaction/doc/images/webengineaction-example.png diff --git a/examples/webengine/webengineaction/doc/src/webengineaction.qdoc b/examples/webengine/webengineaction/doc/src/webengineaction.qdoc new file mode 100644 index 000000000..672f86f0f --- /dev/null +++ b/examples/webengine/webengineaction/doc/src/webengineaction.qdoc @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example webengine/webengineaction + \title WebEngine Action Example + \ingroup webengine-examples + \brief A simple browser implemented using WebEngineActions. + + \image webengineaction-example.png + + \e {WebEngine Action Example} demonstrates how to perform actions on a web page + using the \l{WebEngineAction} type. It shows the minimum amount of code needed + to bind browser functionalities to input elements and build up a custom context + menu. + + \include examples-run.qdocinc + + \section1 Working With Web Engine Actions + + An intended use of \l{WebEngineAction} is building a connection between UI + elements and browser commands. It can be added to menus and toolbars via + assigning its properties to the corresponding ones of the element. + + The \l{ToolButton} relies on the properties provided by a + \l{WebEngineAction}. Clicking the button triggers backwards navigation on the + originating \l{WebEngineView} of the action. + + \quotefromfile webengine/webengineaction/main.qml + \skipto ToolButton { + \printuntil } + + The simplest way to create custom context menus is enumerating the required + \l{WebEngineAction} types in a data model and instantiating \l{MenuItem} types + for them, for example using a \l{Repeater}. + + \quotefromfile webengine/webengineaction/main.qml + \skipto property Menu contextMenu: Menu { + \printuntil /^ {8}\}/ + + Assigning a \l{WebEngineAction} to multiple UI elements will keep them in sync. + As it can be seen in the picture above, if the browser engine disables a + navigation action, both corresponding menu items will be disabled. +*/ diff --git a/examples/webengine/webengineaction/main.cpp b/examples/webengine/webengineaction/main.cpp new file mode 100644 index 000000000..ce723a99b --- /dev/null +++ b/examples/webengine/webengineaction/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <qtwebengineglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QtWebEngine::initialize(); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/examples/webengine/webengineaction/main.qml b/examples/webengine/webengineaction/main.qml new file mode 100644 index 000000000..a3933ee41 --- /dev/null +++ b/examples/webengine/webengineaction/main.qml @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtWebEngine 1.7 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 + +ApplicationWindow { + id: window + visible: true + width: 800 + height: 600 + title: qsTr("WebEngineAction Example") + + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = text + } + + ToolButton { + id: settingsButton + text: "Settings" + icon.name: "settings-configure" + display: AbstractButton.TextUnderIcon + onClicked: settingsMenu.open() + checked: settingsMenu.visible + + Menu { + id: settingsMenu + y: settingsButton.height + + MenuItem { + id: customContextMenuOption + checkable: true + checked: true + + text: "Custom context menu" + } + } + } + } + } + + WebEngineView { + id: webEngineView + url: "https://qt.io" + anchors.fill: parent + + Component.onCompleted: { + profile.downloadRequested.connect(function(download){ + download.accept(); + }) + } + + property Menu contextMenu: Menu { + Repeater { + model: [ + WebEngineView.Back, + WebEngineView.Forward, + WebEngineView.Reload, + WebEngineView.SavePage, + WebEngineView.Copy, + WebEngineView.Paste, + WebEngineView.Cut + ] + MenuItem { + text: webEngineView.action(modelData).text + enabled: webEngineView.action(modelData).enabled + onClicked: webEngineView.action(modelData).trigger() + icon.name: webEngineView.action(modelData).iconName + display: MenuItem.TextBesideIcon + } + } + } + + onContextMenuRequested: function(request) { + if (customContextMenuOption.checked) { + request.accepted = true; + contextMenu.popup(); + } + } + } +} diff --git a/examples/webengine/webengineaction/qml.qrc b/examples/webengine/webengineaction/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/examples/webengine/webengineaction/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/examples/webengine/webengineaction/webengineaction.pro b/examples/webengine/webengineaction/webengineaction.pro new file mode 100644 index 000000000..13cc4602d --- /dev/null +++ b/examples/webengine/webengineaction/webengineaction.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webengine + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webengine/webengineaction +INSTALLS += target diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp index 4351dfe71..5d00cd19a 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp @@ -62,6 +62,7 @@ #include <QMenuBar> #include <QMessageBox> #include <QProgressBar> +#include <QScreen> #include <QStatusBar> #include <QToolBar> #include <QVBoxLayout> @@ -143,7 +144,7 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool QSize BrowserWindow::sizeHint() const { - QRect desktopRect = QApplication::desktop()->screenGeometry(); + QRect desktopRect = QApplication::primaryScreen()->geometry(); QSize size = desktopRect.size() * qreal(0.9); return size; } diff --git a/examples/webenginewidgets/simplebrowser/main.cpp b/examples/webenginewidgets/simplebrowser/main.cpp index 0e8f86c8f..7b77a4bd2 100644 --- a/examples/webenginewidgets/simplebrowser/main.cpp +++ b/examples/webenginewidgets/simplebrowser/main.cpp @@ -52,6 +52,7 @@ #include "browserwindow.h" #include "tabwidget.h" #include <QApplication> +#include <QWebEngineProfile> #include <QWebEngineSettings> QUrl commandLineUrlArgument() @@ -74,6 +75,8 @@ int main(int argc, char **argv) app.setWindowIcon(QIcon(QStringLiteral(":AppLogoColor.png"))); QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true); + QWebEngineProfile::defaultProfile()->setUseForGlobalCertificateVerification(); QUrl url = commandLineUrlArgument(); diff --git a/examples/webenginewidgets/simplebrowser/webpage.cpp b/examples/webenginewidgets/simplebrowser/webpage.cpp index e44410284..99849c77d 100644 --- a/examples/webenginewidgets/simplebrowser/webpage.cpp +++ b/examples/webenginewidgets/simplebrowser/webpage.cpp @@ -138,7 +138,7 @@ inline QString questionForFeature(QWebEnginePage::Feature feature) case QWebEnginePage::DesktopAudioVideoCapture: return WebPage::tr("Allow %1 to capture audio and video of your desktop?"); case QWebEnginePage::Notifications: - return QString(); + return WebPage::tr("Allow %1 to show notification on your desktop?"); } return QString(); } diff --git a/mkspecs/features/gn_generator.prf b/mkspecs/features/gn_generator.prf index c1399a453..e6ae263ac 100644 --- a/mkspecs/features/gn_generator.prf +++ b/mkspecs/features/gn_generator.prf @@ -29,12 +29,12 @@ isEmpty(GN_FIND_MOCABLES_SCRIPT): GN_FIND_MOCABLES_SCRIPT = "//build/gn_find_moc # MOC SETUP GN_CONTENTS += "moc_source_h_files = exec_script(\"$$GN_FIND_MOCABLES_SCRIPT\"," -GN_CONTENTS += " [ \"$$_PRO_FILE_PWD_\"," +GN_CONTENTS += " [" for (headerfile, HEADERS): GN_CONTENTS += " \"$$GN_SRC_DIR/$$headerfile\"," GN_CONTENTS += " ], \"list lines\", [\"$$system_path($$_PRO_FILE_)\"]"\ ")" GN_CONTENTS += "moc_source_cpp_files = exec_script(\"$$GN_FIND_MOCABLES_SCRIPT\"," -GN_CONTENTS += " [ \"$$_PRO_FILE_PWD_\"," +GN_CONTENTS += " [" for (sourcefile, SOURCES): GN_CONTENTS += " \"$$GN_SRC_DIR/$$sourcefile\"," GN_CONTENTS += " ], \"list lines\", [\"$$system_path($$_PRO_FILE_)\"]"\ ")" diff --git a/src/3rdparty b/src/3rdparty -Subproject b3edbf2a84d96ed1d0307a8ef909b2f1f6c2a97 +Subproject 0aae24c2876d19946ce0d28adc38c3dbed2c654 diff --git a/src/buildtools/gn.pro b/src/buildtools/gn.pro index 559cdf183..30ada9357 100644 --- a/src/buildtools/gn.pro +++ b/src/buildtools/gn.pro @@ -18,7 +18,13 @@ build_pass|!debug_and_release { src_3rd_party_dir = $$absolute_path("$${getChromiumSrcDir()}/../", "$$QTWEBENGINE_ROOT") gn_bootstrap = $$system_path($$absolute_path(gn/build/gen.py, $$src_3rd_party_dir)) - gn_configure = $$system_quote($$gn_bootstrap) --no-last-commit-position --out-path $$out_path + gn_configure = $$system_quote($$gn_bootstrap) --no-last-commit-position --out-path $$out_path \ + --cc \"$$which($$QMAKE_CC)\" --cxx \"$$which($$QMAKE_CXX)\" \ + --ld \"$$which($$QMAKE_LINK)\" + macos { + gn_configure += --isysroot \"$$QMAKE_MAC_SDK_PATH\" + } + message($$gn_configure) !system("$$pythonPathForSystem() $$gn_configure") { error("GN generation error!") } diff --git a/src/core/accessibility_activation_observer.cpp b/src/core/accessibility_activation_observer.cpp new file mode 100644 index 000000000..75ad90c54 --- /dev/null +++ b/src/core/accessibility_activation_observer.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "accessibility_activation_observer.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "content/browser/accessibility/browser_accessibility_state_impl.h" + +namespace QtWebEngineCore { + +namespace { + +bool isAccessibilityEnabled() { + // On Linux accessibility is disabled by default due to performance issues, + // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment + // variable. For details, see QTBUG-59922. +#ifdef Q_OS_LINUX + static bool accessibility_enabled + = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); +#else + const bool accessibility_enabled = true; +#endif + return accessibility_enabled; +} + +} // namespace + +AccessibilityActivationObserver::AccessibilityActivationObserver() +{ + if (isAccessibilityEnabled()) { + QAccessible::installActivationObserver(this); + if (QAccessible::isActive()) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + } +} + +AccessibilityActivationObserver::~AccessibilityActivationObserver() +{ + QAccessible::removeActivationObserver(this); +} + +void AccessibilityActivationObserver::accessibilityActiveChanged(bool active) +{ + if (active) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + else + content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); +} + +} // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/core/net/qrc_protocol_handler_qt.cpp b/src/core/accessibility_activation_observer.h index eb716f182..e42c83eb5 100644 --- a/src/core/net/qrc_protocol_handler_qt.cpp +++ b/src/core/accessibility_activation_observer.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,27 +37,27 @@ ** ****************************************************************************/ -#include "qrc_protocol_handler_qt.h" -#include "url_request_qrc_job_qt.h" +#ifndef ACCESSIBILITY_ACTIVATION_OBSERVER_H +#define ACCESSIBILITY_ACTIVATION_OBSERVER_H -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_error_job.h" +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> namespace QtWebEngineCore { -const char kQrcSchemeQt[] = "qrc"; +class RenderWidgetHostViewQt; -QrcProtocolHandlerQt::QrcProtocolHandlerQt() +class AccessibilityActivationObserver : public QAccessible::ActivationObserver { -} +public: + AccessibilityActivationObserver(); + ~AccessibilityActivationObserver(); -net::URLRequestJob *QrcProtocolHandlerQt::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const -{ - if (!networkDelegate) - return new net::URLRequestErrorJob(request, Q_NULLPTR, net::ERR_ACCESS_DENIED); - - return new URLRequestQrcJobQt(request, networkDelegate); -} + void accessibilityActiveChanged(bool active) override; +}; } // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY + +#endif // ACCESSIBILITY_ACTIVATION_OBSERVER_H diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index 4b69b348a..326d4481f 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -32,12 +32,14 @@ gcc: QMAKE_CXXFLAGS_WARN_ON = -Wno-unused-parameter HEADERS = \ qwebenginecallback.h \ qwebenginecallback_p.h \ + qwebengineclientcertificatestore.h \ qtwebenginecoreglobal.h \ qtwebenginecoreglobal_p.h \ qwebenginecookiestore.h \ qwebenginecookiestore_p.h \ qwebenginehttprequest.h \ qwebenginemessagepumpscheduler_p.h \ + qwebenginenotification.h \ qwebenginequotarequest.h \ qwebengineregisterprotocolhandlerrequest.h \ qwebengineurlrequestinterceptor.h \ @@ -49,9 +51,11 @@ HEADERS = \ SOURCES = \ qtwebenginecoreglobal.cpp \ + qwebengineclientcertificatestore.cpp \ qwebenginecookiestore.cpp \ qwebenginehttprequest.cpp \ qwebenginemessagepumpscheduler.cpp \ + qwebenginenotification.cpp \ qwebenginequotarequest.cpp \ qwebengineregisterprotocolhandlerrequest.cpp \ qwebengineurlrequestinfo.cpp \ diff --git a/src/core/api/qwebengineclientcertificatestore.cpp b/src/core/api/qwebengineclientcertificatestore.cpp new file mode 100644 index 000000000..cac8fb151 --- /dev/null +++ b/src/core/api/qwebengineclientcertificatestore.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwebengineclientcertificatestore.h" + +#include "net/client_cert_store_data.h" + +#include <QByteArray> +#include <QList> + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(ssl) + +/*! + \class QWebEngineClientCertificateStore::Entry + \inmodule QtWebEngineCore + \since 5.13 + \brief This structure holds the certificate and the private key. +*/ + +/*! + \class QWebEngineClientCertificateStore + \inmodule QtWebEngineCore + \since 5.13 + \brief The QWebEngineClientCertificateStore class provides an in-memory store for client certificates. + + The class allows to store client certificates in an in-memory store. + When a web site requests an SSL client certificate, the QWebEnginePage::selectClientCertificate + signal is emitted with matching certificates from the native certificate store or the in-memory store. + The getInstance() method can be used to access the single instance of the class. +*/ + +QWebEngineClientCertificateStore::QWebEngineClientCertificateStore(QtWebEngineCore::ClientCertificateStoreData *storeData) + : m_storeData(storeData) +{ +} + +/*! + Destroys this QWebEngineClientCertificateStore object. +*/ + +QWebEngineClientCertificateStore::~QWebEngineClientCertificateStore() +{ + // Just in case user has not deleted in-memory certificates + clear(); +} + +/*! + Adds a \a certificate with the \a privateKey to the in-memory client certificate store. +*/ + +void QWebEngineClientCertificateStore::add(const QSslCertificate &certificate, const QSslKey &privateKey) +{ + m_storeData->add(certificate, privateKey); +} + +/*! + Returns a list of private and public keys of client certificates in the in-memory store. + Returns an empty list if the in-memory store does not contain certificates. +*/ + +QList<QWebEngineClientCertificateStore::Entry> QWebEngineClientCertificateStore::toList() const +{ + QList<Entry> certificateList; + for (auto data : qAsConst(m_storeData->extraCerts)) { + Entry entry; + entry.certificate = data->certificate; + entry.privateKey = data->key; + certificateList.append(entry); + } + return certificateList; +} + +/*! + Deletes all the instances of the client certificate in the in-memory client certificate store + that matches the certificate \a certificate. +*/ + +void QWebEngineClientCertificateStore::remove(const QSslCertificate &certificate) +{ + m_storeData->remove(certificate); +} + +/*! + Clears all the client certificates from the in-memory store. +*/ + +void QWebEngineClientCertificateStore::clear() +{ + m_storeData->clear(); +} + +#endif // QT_CONFIG(ssl) + +QT_END_NAMESPACE diff --git a/src/core/api/qwebengineclientcertificatestore.h b/src/core/api/qwebengineclientcertificatestore.h new file mode 100644 index 000000000..441a5f913 --- /dev/null +++ b/src/core/api/qwebengineclientcertificatestore.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWEBENGINECLIENTCERTIFICATESTORE_H +#define QWEBENGINECLIENTCERTIFICATESTORE_H + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +#include <QtCore/qscopedpointer.h> +#include <QtNetwork/qsslcertificate.h> +#include <QtNetwork/qsslkey.h> + +namespace QtWebEngineCore { +struct ClientCertificateStoreData; +class ProfileAdapter; +} + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(ssl) + +class QWebEngineProfile; +class QWEBENGINECORE_EXPORT QWebEngineClientCertificateStore { + +public: + struct Entry { + QSslKey privateKey; + QSslCertificate certificate; + }; + + void add(const QSslCertificate &certificate, const QSslKey &privateKey); + QList<Entry> toList() const; + void remove(const QSslCertificate &certificate); + void clear(); + +private: + friend class QtWebEngineCore::ProfileAdapter; + Q_DISABLE_COPY(QWebEngineClientCertificateStore) + + QWebEngineClientCertificateStore(QtWebEngineCore::ClientCertificateStoreData *storeData); + ~QWebEngineClientCertificateStore(); + QtWebEngineCore::ClientCertificateStoreData *m_storeData; +}; + +#endif // QT_CONFIG(ssl) + +QT_END_NAMESPACE + +#endif // QWebEngineClientCertificateStore_H diff --git a/src/core/api/qwebenginenotification.cpp b/src/core/api/qwebenginenotification.cpp new file mode 100644 index 000000000..0b91cf273 --- /dev/null +++ b/src/core/api/qwebenginenotification.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwebenginenotification.h" + +#include "user_notification_controller.h" + +#include <QExplicitlySharedDataPointer> + +QT_BEGIN_NAMESPACE + +using QtWebEngineCore::UserNotificationController; + +/*! + \class QWebEngineNotification + \brief The QWebEngineNotification class encapsulates the data of an HTML5 web notification. + \since 5.13 + + \inmodule QtWebEngineCore + + This class contains the information and API for HTML5 desktop and push notifications. +*/ + +class QWebEngineNotificationPrivate : public UserNotificationController::Client { +public: + QWebEngineNotificationPrivate(QWebEngineNotification *q, const QSharedPointer<UserNotificationController> &controller) + : controller(controller) + , q(q) + { + controller->setClient(this); + } + ~QWebEngineNotificationPrivate() override + { + if (controller->client() == this) + controller->setClient(0); + } + + // UserNotificationController::Client: + virtual void notificationClosed(const UserNotificationController *) Q_DECL_OVERRIDE + { + Q_EMIT q->closed(); + } + + QSharedPointer<UserNotificationController> controller; + QWebEngineNotification *q; +}; + + +/*! + Creates a null QWebEngineNotification. + + \sa isNull() +*/ +QWebEngineNotification::QWebEngineNotification() { } + +/*! \internal +*/ +QWebEngineNotification::QWebEngineNotification(const QSharedPointer<UserNotificationController> &controller) + : d_ptr(new QWebEngineNotificationPrivate(this, controller)) +{ } + +/*! \internal +*/ +QWebEngineNotification::QWebEngineNotification(const QWebEngineNotification &other) + : QObject() + , d_ptr(new QWebEngineNotificationPrivate(this, other.d_ptr->controller)) +{ } + +/*! \internal +*/ +QWebEngineNotification::~QWebEngineNotification() +{ +} + +/*! \internal +*/ +const QWebEngineNotification &QWebEngineNotification::operator=(const QWebEngineNotification &other) +{ + d_ptr.reset(new QWebEngineNotificationPrivate(this, other.d_ptr->controller)); + return *this; +} + +/*! + Returns \c true if the two notifications belong to the same message chain. + That is, if their tag() and origin() are the same. This means one is + a replacement or an update of the \a other. + + \sa tag(), origin() +*/ +bool QWebEngineNotification::matches(const QWebEngineNotification &other) const +{ + if (!d_ptr) + return !other.d_ptr; + if (!other.d_ptr) + return false; + return tag() == other.tag() && origin() == other.origin(); +} + +/*! + \property QWebEngineNotification::title + \brief The title of the notification. + \sa message() +*/ +QString QWebEngineNotification::title() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->title() : QString(); +} + +/*! + \property QWebEngineNotification::message + \brief The body of the notification message. + \sa title() +*/ + +QString QWebEngineNotification::message() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->body() : QString(); +} + +/*! + \property QWebEngineNotification::tag + \brief The tag of the notification message. + + New notifications that have the same tag and origin URL as an existing + one should replace or update the old notification with the same tag. + + \sa matches() +*/ +QString QWebEngineNotification::tag() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->tag() : QString(); +} + +/*! + \property QWebEngineNotification::origin + \brief The URL of the page sending the notification. +*/ + +QUrl QWebEngineNotification::origin() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->origin() : QUrl(); +} + +/*! + \property QWebEngineNotification::icon + \brief The icon to be shown with the notification. + + If no icon is set by the sender, an null QIcon is returned. +*/ +QIcon QWebEngineNotification::icon() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->icon() : QIcon(); +} + +/*! + \property QWebEngineNotification::language + \brief The primary language for the notification's title and body. + + Its value is a valid BCP 47 language tag, or the empty string. + + \sa title(), message() +*/ +QString QWebEngineNotification::language() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->language() : QString(); +} + +/*! + \property QWebEngineNotification::direction + \brief The text direction for the notification's title and body. + \sa title(), message() +*/ +QWebEngineNotification::Direction QWebEngineNotification::direction() const +{ + Q_D(const QWebEngineNotification); + return d ? static_cast<Direction>(d->controller->direction()) : DirectionAuto; +} + +/*! + Returns \c true if the notification is a default constructed null notification. +*/ +bool QWebEngineNotification::isNull() const +{ + return d_ptr.isNull(); +} + +/*! + Creates and dispatches a JavaScript \e {show event} on notification. + + Should be called by the notification platform when the notification has been shown to user. +*/ +void QWebEngineNotification::show() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationDisplayed(); +} + +/*! + Creates and dispatches a JavaScript \e {click event} on notification. + + Should be called by the notification platform when the notification is activated by the user. +*/ +void QWebEngineNotification::click() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationClicked(); +} + +/*! + Creates and dispatches a JavaScript \e {close event} on notification. + + Should be called by the notification platform when the notification is closed, + either by the underlying platform or by the user. +*/ +void QWebEngineNotification::close() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationClosed(); +} + +/*! + \fn void QWebEngineNotification::closed() + + This signal is emitted when the web page calls close steps for the notification, + and it no longer needs to be shown. +*/ + +QT_END_NAMESPACE + +#include "moc_qwebenginenotification.cpp" diff --git a/src/core/api/qwebenginenotification.h b/src/core/api/qwebenginenotification.h new file mode 100644 index 000000000..b6b7414f9 --- /dev/null +++ b/src/core/api/qwebenginenotification.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWEBENGINENOTIFICATION_H +#define QWEBENGINENOTIFICATION_H + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +#include <QtCore/QObject> +#include <QtCore/QScopedPointer> +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtGui/QIcon> + +namespace QtWebEngineCore { +class UserNotificationController; +} + +QT_BEGIN_NAMESPACE + +class QWebEngineNotificationPrivate; + +class QWEBENGINECORE_EXPORT QWebEngineNotification : public QObject { + Q_OBJECT + Q_PROPERTY(QUrl origin READ origin CONSTANT FINAL) + Q_PROPERTY(QIcon icon READ icon CONSTANT FINAL) + Q_PROPERTY(QString title READ title CONSTANT FINAL) + Q_PROPERTY(QString message READ message CONSTANT FINAL) + Q_PROPERTY(QString tag READ tag CONSTANT FINAL) + Q_PROPERTY(QString language READ language CONSTANT FINAL) + Q_PROPERTY(Direction direction READ direction CONSTANT FINAL) + +public: + QWebEngineNotification(); + QWebEngineNotification(const QWebEngineNotification &); + virtual ~QWebEngineNotification(); + const QWebEngineNotification &operator=(const QWebEngineNotification &); + + enum Direction { + LeftToRight = Qt::LeftToRight, + RightToLeft = Qt::RightToLeft, + DirectionAuto = Qt::LayoutDirectionAuto + }; + Q_ENUM(Direction) + + bool matches(const QWebEngineNotification &) const; + + QUrl origin() const; + QIcon icon() const; + QString title() const; + QString message() const; + QString tag() const; + QString language() const; + Direction direction() const; + + bool isNull() const; + +public Q_SLOTS: + void show() const; + void click() const; + void close() const; + +Q_SIGNALS: + void closed(); + +private: + QWebEngineNotification(const QSharedPointer<QtWebEngineCore::UserNotificationController> &); + Q_DECLARE_PRIVATE(QWebEngineNotification) + QScopedPointer<QWebEngineNotificationPrivate> d_ptr; + friend class QQuickWebEngineProfilePrivate; + friend class QWebEngineProfilePrivate; +}; + +QT_END_NAMESPACE + +#endif // QWEBENGINENOTIFICATION_H diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index ea9081fc1..dc2d07740 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -96,8 +96,8 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q interceptor on the profile enables intercepting, blocking, and modifying URL requests before they reach the networking stack of Chromium. - You can install the interceptor on a profile via QWebEngineProfile::setRequestInterceptor() - or QQuickWebEngineProfile::setRequestInterceptor(). + You can install the interceptor on a profile via QWebEngineProfile::setUrlRequestInterceptor() + or QQuickWebEngineProfile::setUrlRequestInterceptor(). When using the \l{Qt WebEngine Widgets Module}, \l{QWebEnginePage::acceptNavigationRequest()} offers further options to accept or block requests. @@ -115,8 +115,7 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q \fn void QWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) Reimplementing this virtual function makes it possible to intercept URL - requests. This function is executed on the IO thread, and therefore running - long tasks here will block networking. + requests. This method will be stalling the URL request until handled. \a info contains the information about the URL request and will track internally whether its members have been altered. @@ -140,10 +139,17 @@ QWebEngineUrlRequestInfoPrivate::QWebEngineUrlRequestInfoPrivate(QWebEngineUrlRe /*! \internal */ +QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p) + : d_ptr(p.d_ptr.take()) +{ +} + +/*! + \internal +*/ QWebEngineUrlRequestInfo::~QWebEngineUrlRequestInfo() { - } /*! @@ -260,6 +266,14 @@ bool QWebEngineUrlRequestInfo::changed() const } /*! + \internal +*/ +void QWebEngineUrlRequestInfo::resetChanged() +{ + d_ptr->changed = false; +} + +/*! Redirects this request to \a url. It is only possible to redirect requests that do not have payload data, such as GET requests. */ diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h index 68c46dcf4..e1f9ca6ef 100644 --- a/src/core/api/qwebengineurlrequestinfo.h +++ b/src/core/api/qwebengineurlrequestinfo.h @@ -47,6 +47,7 @@ namespace QtWebEngineCore { class NetworkDelegateQt; +class URLRequestNotification; } QT_BEGIN_NAMESPACE @@ -104,10 +105,14 @@ public: private: friend class QtWebEngineCore::NetworkDelegateQt; + friend class QtWebEngineCore::URLRequestNotification; Q_DISABLE_COPY(QWebEngineUrlRequestInfo) Q_DECLARE_PRIVATE(QWebEngineUrlRequestInfo) + void resetChanged(); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPrivate *p); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p); ~QWebEngineUrlRequestInfo(); QScopedPointer<QWebEngineUrlRequestInfoPrivate> d_ptr; }; diff --git a/src/core/api/qwebengineurlrequestinterceptor.h b/src/core/api/qwebengineurlrequestinterceptor.h index dc2a15ee3..2b07681ca 100644 --- a/src/core/api/qwebengineurlrequestinterceptor.h +++ b/src/core/api/qwebengineurlrequestinterceptor.h @@ -55,7 +55,7 @@ class QWEBENGINECORE_EXPORT QWebEngineUrlRequestInterceptor : public QObject Q_OBJECT Q_DISABLE_COPY(QWebEngineUrlRequestInterceptor) public: - explicit QWebEngineUrlRequestInterceptor(QObject *p = Q_NULLPTR) + explicit QWebEngineUrlRequestInterceptor(QObject *p = nullptr) : QObject (p) { } diff --git a/src/core/api/qwebengineurlrequestjob.cpp b/src/core/api/qwebengineurlrequestjob.cpp index c3541598b..bc860b3b3 100644 --- a/src/core/api/qwebengineurlrequestjob.cpp +++ b/src/core/api/qwebengineurlrequestjob.cpp @@ -140,6 +140,15 @@ QUrl QWebEngineUrlRequestJob::initiator() const } /*! + \since 5.13 + Returns any HTTP headers added to the request. +*/ +QMap<QByteArray, QByteArray> QWebEngineUrlRequestJob::requestHeaders() const +{ + return d_ptr->requestHeaders(); +} + +/*! Replies to the request with \a device and the MIME type \a contentType. The user has to be aware that \a device will be used on another thread diff --git a/src/core/api/qwebengineurlrequestjob.h b/src/core/api/qwebengineurlrequestjob.h index 7ce8be7ec..b2dd8baa3 100644 --- a/src/core/api/qwebengineurlrequestjob.h +++ b/src/core/api/qwebengineurlrequestjob.h @@ -73,6 +73,7 @@ public: QUrl requestUrl() const; QByteArray requestMethod() const; QUrl initiator() const; + QMap<QByteArray, QByteArray> requestHeaders() const; void reply(const QByteArray &contentType, QIODevice *device); void fail(Error error); diff --git a/src/core/api/qwebengineurlschemehandler.cpp b/src/core/api/qwebengineurlschemehandler.cpp index b5912703d..aecee5044 100644 --- a/src/core/api/qwebengineurlschemehandler.cpp +++ b/src/core/api/qwebengineurlschemehandler.cpp @@ -111,7 +111,6 @@ QWebEngineUrlSchemeHandler::QWebEngineUrlSchemeHandler(QObject *parent) */ QWebEngineUrlSchemeHandler::~QWebEngineUrlSchemeHandler() { - Q_EMIT _q_destroyedUrlSchemeHandler(this); } /*! diff --git a/src/core/api/qwebengineurlschemehandler.h b/src/core/api/qwebengineurlschemehandler.h index 23fee4b95..549778561 100644 --- a/src/core/api/qwebengineurlschemehandler.h +++ b/src/core/api/qwebengineurlschemehandler.h @@ -60,11 +60,6 @@ public: virtual void requestStarted(QWebEngineUrlRequestJob*) = 0; -#ifndef Q_QDOC -Q_SIGNALS: - void _q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*); -#endif - private: Q_DISABLE_COPY(QWebEngineUrlSchemeHandler) }; diff --git a/src/core/authentication_dialog_controller.cpp b/src/core/authentication_dialog_controller.cpp index bd23d1768..1133b0bc1 100644 --- a/src/core/authentication_dialog_controller.cpp +++ b/src/core/authentication_dialog_controller.cpp @@ -40,7 +40,9 @@ #include "authentication_dialog_controller.h" #include "authentication_dialog_controller_p.h" +#include "base/task/post_task.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/browser_task_traits.h" namespace QtWebEngineCore { @@ -51,9 +53,10 @@ AuthenticationDialogControllerPrivate::AuthenticationDialogControllerPrivate(Log void AuthenticationDialogControllerPrivate::dialogFinished(bool accepted, const QString &user, const QString &password) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&LoginDelegateQt::sendAuthToRequester, loginDelegate, accepted, user, password)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&LoginDelegateQt::sendAuthToRequester, + loginDelegate, accepted, user, password)); } AuthenticationDialogController::AuthenticationDialogController(AuthenticationDialogControllerPrivate *dd) diff --git a/src/core/browser_accessibility_qt.cpp b/src/core/browser_accessibility_qt.cpp index 29fbbc542..75527ea95 100644 --- a/src/core/browser_accessibility_qt.cpp +++ b/src/core/browser_accessibility_qt.cpp @@ -61,6 +61,11 @@ const BrowserAccessibilityQt *ToBrowserAccessibilityQt(const BrowserAccessibilit return static_cast<const BrowserAccessibilityQt *>(obj); } +QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *obj) +{ + return static_cast<BrowserAccessibilityQt *>(obj); +} + BrowserAccessibilityQt::BrowserAccessibilityQt() { QAccessible::registerAccessibleInterface(this); @@ -357,6 +362,8 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::EditableText; case ax::mojom::Role::kInputTime: return QAccessible::SpinBox; + case ax::mojom::Role::kKeyboard: + return QAccessible::NoRole; // FIXME case ax::mojom::Role::kLabelText: return QAccessible::StaticText; case ax::mojom::Role::kLayoutTable: diff --git a/src/core/browser_accessibility_qt.h b/src/core/browser_accessibility_qt.h index 345ee9862..decfc1e9d 100644 --- a/src/core/browser_accessibility_qt.h +++ b/src/core/browser_accessibility_qt.h @@ -146,6 +146,7 @@ public: }; const BrowserAccessibilityQt *ToBrowserAccessibilityQt(const BrowserAccessibility *obj); +QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *acc); } // namespace content diff --git a/src/core/browser_main_parts_qt.cpp b/src/core/browser_main_parts_qt.cpp index 3d85c04c4..4dbff086b 100644 --- a/src/core/browser_main_parts_qt.cpp +++ b/src/core/browser_main_parts_qt.cpp @@ -46,7 +46,16 @@ #include "base/threading/thread_restrictions.h" #include "content/public/browser/browser_main_parts.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_security_policy.h" #include "content/public/common/service_manager_connection.h" +#include "extensions/buildflags/buildflags.h" +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/common/constants.h" +#include "extensions/common/extensions_client.h" +#include "extensions/extensions_browser_client_qt.h" +#include "extensions/extension_system_factory_qt.h" +#include "common/extensions/extensions_client_qt.h" +#endif //BUILDFLAG(ENABLE_EXTENSIONS) #include "services/resource_coordinator/public/cpp/process_resource_coordinator.h" #include "services/resource_coordinator/public/cpp/resource_coordinator_features.h" #include "services/service_manager/public/cpp/connector.h" @@ -57,6 +66,12 @@ #include "web_engine_context.h" #include <QEventLoop> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +#include "ui/gl/gl_context.h" +#include <QOpenGLContext> +#endif #if defined(OS_MACOSX) #include "ui/base/idle/idle.h" @@ -131,8 +146,55 @@ public: } private: + // Both Qt and Chromium keep track of the current GL context by using + // thread-local variables, and naturally they are completely unaware of each + // other. As a result, when a QOpenGLContext is made current, the previous + // gl::GLContext is not released, and vice-versa. This is fine as long as + // each thread uses exclusively either Qt or Chromium GL bindings, which is + // usually the case. + // + // The only exception is when the GL driver is considered thread-unsafe + // (QOpenGLContext::supportsThreadedOpenGL() is false), in which case we + // have to run all GL operations, including Chromium's GPU service, on the + // UI thread. Now the bindings are being mixed and both Qt and Chromium get + // quite confused regarding the current state of the surface. + // + // To get this to work we have to release the current QOpenGLContext before + // executing any tasks from Chromium's GPU service and the gl::GLContext + // afterwards. Since GPU service just posts tasks to the UI thread task + // runner, we'll have to instrument the entire UI thread message pump. + class ScopedGLContextChecker + { +#if QT_CONFIG(opengl) + public: + ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (QOpenGLContext *context = QOpenGLContext::currentContext()) + context->doneCurrent(); + } + + ~ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (gl::GLContext *context = gl::GLContext::GetCurrent()) + context->ReleaseCurrent(nullptr); + } + + private: + bool m_enabled = !QOpenGLContext::supportsThreadedOpenGL(); +#endif // QT_CONFIG(opengl) + }; + + void handleScheduledWork() { + ScopedGLContextChecker glContextChecker; + bool more_work_is_plausible = m_delegate->DoWork(); base::TimeTicks delayed_work_time; @@ -168,6 +230,9 @@ BrowserMainPartsQt::~BrowserMainPartsQt() = default; int BrowserMainPartsQt::PreEarlyInitialization() { +#if BUILDFLAG(ENABLE_EXTENSIONS) + content::ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme(extensions::kExtensionScheme); +#endif //ENABLE_EXTENSIONS base::MessageLoop::InitMessagePumpForUIFactory(messagePumpFactory); return 0; } @@ -176,6 +241,15 @@ void BrowserMainPartsQt::PreMainMessageLoopStart() { } +void BrowserMainPartsQt::PreMainMessageLoopRun() +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionsClient::Set(new extensions::ExtensionsClientQt()); + extensions::ExtensionsBrowserClient::Set(new extensions::ExtensionsBrowserClientQt()); + extensions::ExtensionSystemFactoryQt::GetInstance(); +#endif //ENABLE_EXTENSIONS +} + void BrowserMainPartsQt::PostMainMessageLoopRun() { // The ProfileQt's destructor uses the MessageLoop so it should be deleted @@ -204,11 +278,9 @@ void BrowserMainPartsQt::ServiceManagerConnectionStarted(content::ServiceManager { ServiceQt::GetInstance()->InitConnector(); connection->GetConnector()->StartService(service_manager::Identity("qtwebengine")); - if (resource_coordinator::IsResourceCoordinatorEnabled()) { - m_processResourceCoordinator = std::make_unique<resource_coordinator::ProcessResourceCoordinator>(connection->GetConnector()); - m_processResourceCoordinator->SetLaunchTime(base::Time::Now()); - m_processResourceCoordinator->SetPID(base::Process::Current().Pid()); - } + m_processResourceCoordinator = std::make_unique<resource_coordinator::ProcessResourceCoordinator>(connection->GetConnector()); + m_processResourceCoordinator->SetLaunchTime(base::Time::Now()); + m_processResourceCoordinator->SetPID(base::Process::Current().Pid()); } } // namespace QtWebEngineCore diff --git a/src/core/browser_main_parts_qt.h b/src/core/browser_main_parts_qt.h index 04ca9483d..374220c3b 100644 --- a/src/core/browser_main_parts_qt.h +++ b/src/core/browser_main_parts_qt.h @@ -66,6 +66,7 @@ public: int PreEarlyInitialization() override; void PreMainMessageLoopStart() override; + void PreMainMessageLoopRun() override; void PostMainMessageLoopRun() override; int PreCreateThreads() override; void ServiceManagerConnectionStarted(content::ServiceManagerConnection *connection) override; diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp index 841dcf4c9..09058301b 100644 --- a/src/core/chromium_overrides.cpp +++ b/src/core/chromium_overrides.cpp @@ -37,8 +37,6 @@ ** ****************************************************************************/ -#include "chromium_overrides.h" - #include "ozone/gl_context_qt.h" #include "qtwebenginecoreglobal_p.h" #include "web_contents_view_qt.h" @@ -59,6 +57,7 @@ #include <QWindow> #include <QFontDatabase> #include <QStringList> +#include <QLibraryInfo> #if defined(USE_AURA) && !defined(USE_OZONE) #include "ui/base/dragdrop/os_exchange_data.h" @@ -71,27 +70,6 @@ #include "net/ssl/openssl_client_key_store.h" #endif -namespace QtWebEngineCore { -void GetScreenInfoFromNativeWindow(QWindow* window, content::ScreenInfo* results) -{ - QScreen* screen = window->screen(); - if (!screen) - return; - content::ScreenInfo r; - r.device_scale_factor = screen->devicePixelRatio(); - r.depth_per_component = 8; - r.depth = screen->depth(); - r.is_monochrome = (r.depth == 1); - - QRect screenGeometry = screen->geometry(); - r.rect = gfx::Rect(screenGeometry.x(), screenGeometry.y(), screenGeometry.width(), screenGeometry.height()); - QRect available = screen->availableGeometry(); - r.available_rect = gfx::Rect(available.x(), available.y(), available.width(), available.height()); - *results = r; -} - -} // namespace QtWebEngineCore - void *GetQtXDisplay() { return GLContextHelper::getXDisplay(); @@ -112,12 +90,13 @@ WebContentsView* CreateWebContentsView(WebContentsImpl *web_contents, return rv; } -// static -void WebContentsView::GetDefaultScreenInfo(content::ScreenInfo* results) +#if defined(Q_OS_MACOS) +std::string getQtPrefix() { - QWindow dummy; - QtWebEngineCore::GetScreenInfoFromNativeWindow(&dummy, results); + const QString prefix = QLibraryInfo::location(QLibraryInfo::PrefixPath); + return prefix.toStdString(); } +#endif } // namespace content diff --git a/src/core/client_cert_select_controller.cpp b/src/core/client_cert_select_controller.cpp index 7d08d57c1..0baaf2bc5 100644 --- a/src/core/client_cert_select_controller.cpp +++ b/src/core/client_cert_select_controller.cpp @@ -48,6 +48,8 @@ #include "type_conversion.h" +#include <QDebug> + QT_BEGIN_NAMESPACE using namespace QtWebEngineCore; @@ -76,17 +78,40 @@ ClientCertSelectController::~ClientCertSelectController() void ClientCertSelectController::selectNone() { if (m_selected) { - qWarning() << "ClientCertSelectController::selectNone() certicate already selected"; + LOG(WARNING) << "ClientCertSelectController::selectNone() certificate already selected"; return; } m_selected = true; m_delegate->ContinueWithCertificate(nullptr, nullptr); } +void ClientCertSelectController::select(int index) +{ + if (m_selected) { + LOG(WARNING) << "ClientCertSelectController::select() certificate already selected"; + return; + } + for (auto &certInfo : m_clientCerts) { + if (index == 0) { + m_selected = true; + scoped_refptr<net::X509Certificate> cert = certInfo->certificate(); + net::ClientCertIdentity::SelfOwningAcquirePrivateKey( + std::move(certInfo), + base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate, + base::Passed(std::move(m_delegate)), std::move(cert))); + return; + } + std::vector<std::string> pem_encoded; + if (certInfo->certificate()->GetPEMEncodedChain(&pem_encoded)) + --index; + } + LOG(WARNING) << "ClientCertSelectController::select() index out of range:" << index; +} + void ClientCertSelectController::select(const QSslCertificate &certificate) { if (m_selected) { - qWarning() << "ClientCertSelectController::select() certicate already selected"; + LOG(WARNING) << "ClientCertSelectController::select() certificate already selected"; return; } QByteArray derCertificate = certificate.toDer(); @@ -103,19 +128,20 @@ void ClientCertSelectController::select(const QSslCertificate &certificate) return; } } - qWarning() << "ClientCertSelectController::select() - selected client certificate not recognized." - << " Selected certificate needs to be one of the offered"; + LOG(WARNING) << "ClientCertSelectController::select() - selected client certificate not recognized." + << " Selected certificate needs to be one of the offered"; } QVector<QSslCertificate> ClientCertSelectController::certificates() const { - QVector<QSslCertificate> out; + if (!m_certificates.isEmpty()) + return m_certificates; for (auto &cert : m_clientCerts) { std::vector<std::string> pem_encoded; if (cert->certificate()->GetPEMEncodedChain(&pem_encoded)) - out.append(QSslCertificate(QByteArray::fromStdString(pem_encoded.front()))); + m_certificates.append(QSslCertificate(QByteArray::fromStdString(pem_encoded.front()))); } - return out; + return m_certificates; } #endif // !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) diff --git a/src/core/client_cert_select_controller.h b/src/core/client_cert_select_controller.h index 46324ee90..8f4f78d94 100644 --- a/src/core/client_cert_select_controller.h +++ b/src/core/client_cert_select_controller.h @@ -83,6 +83,7 @@ public: #if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) void selectNone(); void select(const QSslCertificate &certificate); + void select(int index); QVector<QSslCertificate> certificates() const; #endif @@ -91,6 +92,7 @@ private: QUrl m_hostAndPort; std::vector<std::unique_ptr<net::ClientCertIdentity>> m_clientCerts; std::unique_ptr<content::ClientCertificateDelegate> m_delegate; + mutable QVector<QSslCertificate> m_certificates; bool m_selected; }; diff --git a/src/core/color_chooser_controller.cpp b/src/core/color_chooser_controller.cpp index 26d675908..361ff93ba 100644 --- a/src/core/color_chooser_controller.cpp +++ b/src/core/color_chooser_controller.cpp @@ -43,6 +43,9 @@ #include "color_chooser_controller_p.h" #include "type_conversion.h" +#include <QColor> +#include <QVariant> + namespace QtWebEngineCore { ColorChooserControllerPrivate::ColorChooserControllerPrivate(content::WebContents *content, const QColor &color) diff --git a/src/core/color_chooser_controller.h b/src/core/color_chooser_controller.h index 4c1b81a9a..e5daa09a8 100644 --- a/src/core/color_chooser_controller.h +++ b/src/core/color_chooser_controller.h @@ -55,6 +55,9 @@ #include <QObject> +QT_FORWARD_DECLARE_CLASS(QColor) +QT_FORWARD_DECLARE_CLASS(QVariant) + namespace QtWebEngineCore { class ColorChooserControllerPrivate; diff --git a/src/core/command_line_pref_store_qt.cpp b/src/core/command_line_pref_store_qt.cpp new file mode 100644 index 000000000..5c5c82e1a --- /dev/null +++ b/src/core/command_line_pref_store_qt.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "command_line_pref_store_qt.h" + +#include "chrome/common/chrome_switches.h" +#include "components/proxy_config/proxy_config_dictionary.h" +#include "components/proxy_config/proxy_config_pref_names.h" +#include "content/public/common/content_switches.h" +#include <QDebug> + +CommandLinePrefStoreQt::CommandLinePrefStoreQt(const base::CommandLine *commandLine) + : CommandLinePrefStore(commandLine) +{ + + if (commandLine->HasSwitch(switches::kNoProxyServer)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateDirect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyPacUrl)) { + std::string pac_script_url = + commandLine->GetSwitchValueASCII(switches::kProxyPacUrl); + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript( + pac_script_url, false)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyAutoDetect)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>( + ProxyConfigDictionary::CreateAutoDetect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyServer)) { + std::string proxy_server = + commandLine->GetSwitchValueASCII(switches::kProxyServer); + std::string bypass_list = + commandLine->GetSwitchValueASCII(switches::kProxyBypassList); + SetValue( + proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers( + proxy_server, bypass_list)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + + if (commandLine->HasSwitch(switches::kNoProxyServer) && (commandLine->HasSwitch(switches::kProxyAutoDetect) || commandLine->HasSwitch(switches::kProxyServer) || commandLine->HasSwitch(switches::kProxyPacUrl) || commandLine->HasSwitch(switches::kProxyBypassList))) { + qWarning("Additional command-line proxy switches specified when --%s was also specified", + qPrintable(switches::kNoProxyServer)); + } +} + +CommandLinePrefStoreQt::~CommandLinePrefStoreQt() = default; diff --git a/src/core/command_line_pref_store_qt.h b/src/core/command_line_pref_store_qt.h new file mode 100644 index 000000000..a509f8ca9 --- /dev/null +++ b/src/core/command_line_pref_store_qt.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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 COMMAND_LINE_PREF_STORE_QT_H +#define COMMAND_LINE_PREF_STORE_QT_H + +#include "base/command_line.h" +#include "components/prefs/command_line_pref_store.h" + +class CommandLinePrefStoreQt : public CommandLinePrefStore +{ +public: + explicit CommandLinePrefStoreQt(const base::CommandLine *commandLine); + +protected: + ~CommandLinePrefStoreQt() override; + DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStoreQt); +}; + +#endif // COMMAND_LINE_PREF_STORE_QT_H diff --git a/src/core/common/extensions/api/qtwebengine_extensions_features.gni b/src/core/common/extensions/api/qtwebengine_extensions_features.gni new file mode 100644 index 000000000..ed7e713c6 --- /dev/null +++ b/src/core/common/extensions/api/qtwebengine_extensions_features.gni @@ -0,0 +1,25 @@ +import("//tools/json_schema_compiler/json_features.gni") + +json_features("qt_api_features") { + feature_type = "APIFeature" + method_name = "AddQtAPIFeatures" + sources = [ + "//extensions/common/api/_webengine_api_features.json" + ] +} + +json_features("qt_permission_features") { + feature_type = "PermissionFeature" + method_name = "AddQtPermissionFeatures" + sources = [ + "//extensions/common/api/_permission_features.json" + ] +} + +group("qtwebengine_extensions_features") { + public_deps = [ + ":qt_api_features", + ":qt_permission_features", + "//extensions/common/api:extensions_features", + ] +} diff --git a/src/core/common/extensions/extensions_api_provider_qt.cpp b/src/core/common/extensions/extensions_api_provider_qt.cpp new file mode 100644 index 000000000..aa746133e --- /dev/null +++ b/src/core/common/extensions/extensions_api_provider_qt.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "extensions_api_provider_qt.h" + +#include "chrome/grit/common_resources.h" + +#include "extensions/common/api/api_features.h" +#include "extensions/common/api/behavior_features.h" +#include "extensions/common/api/generated_schemas.h" +#include "extensions/common/api/manifest_features.h" +#include "extensions/common/api/permission_features.h" +#include "extensions/common/common_manifest_handlers.h" +#include "extensions/common/features/feature_provider.h" +#include "extensions/common/features/json_feature_provider_source.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/grit/extensions_resources.h" + +#include "qt_api_features.h" +//#include "qt_behavior_features.h" +#include "qt_permission_features.h" +//#include "qt_manifest_features.h" + + +namespace extensions { + +ExtensionsAPIProviderQt::ExtensionsAPIProviderQt() +{ + +} + +void ExtensionsAPIProviderQt::RegisterManifestHandlers() +{ + RegisterCommonManifestHandlers(); +} + +void ExtensionsAPIProviderQt::AddAPIFeatures(FeatureProvider *provider) +{ + AddCoreAPIFeatures(provider); + AddQtAPIFeatures(provider); +} + +void ExtensionsAPIProviderQt::AddAPIJSONSources(JSONFeatureProviderSource *json_source) +{ + json_source->LoadJSON(IDR_EXTENSION_API_FEATURES); + json_source->LoadJSON(IDR_CHROME_EXTENSION_API_FEATURES); +} + +void ExtensionsAPIProviderQt::AddPermissionFeatures(FeatureProvider *provider) +{ + AddQtPermissionFeatures(provider); +} + +bool ExtensionsAPIProviderQt::IsAPISchemaGenerated(const std::string &name) +{ + return api::GeneratedSchemas::IsGenerated(name); +} + +base::StringPiece ExtensionsAPIProviderQt::GetAPISchema(const std::string &name) +{ + return api::GeneratedSchemas::Get(name); +} + +void ExtensionsAPIProviderQt::RegisterPermissions(PermissionsInfo* permissions_info) +{ + +} + +} diff --git a/src/core/common/extensions/extensions_api_provider_qt.h b/src/core/common/extensions/extensions_api_provider_qt.h new file mode 100644 index 000000000..8b85e5de2 --- /dev/null +++ b/src/core/common/extensions/extensions_api_provider_qt.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef EXTENSIONS_API_PROVIDER_QT_H +#define EXTENSIONS_API_PROVIDER_QT_H + +#include "extensions/common/extensions_api_provider.h" + +#include "base/macros.h" + +namespace extensions { + +class ExtensionsAPIProviderQt : public ExtensionsAPIProvider +{ +public: + ExtensionsAPIProviderQt(); + + void RegisterManifestHandlers() override; + void AddAPIFeatures(FeatureProvider *provider) override; + void AddAPIJSONSources(JSONFeatureProviderSource* json_source) override; + void AddPermissionFeatures(FeatureProvider* provider) override; + + bool IsAPISchemaGenerated(const std::string& name) override; + base::StringPiece GetAPISchema(const std::string& name) override; + + + // Adds feature definitions to the given |provider| of the specified type. + void AddManifestFeatures(FeatureProvider* provider) override { } + void AddBehaviorFeatures(FeatureProvider* provider) override { } + + + // Registers permissions for any associated API features. + void RegisterPermissions(PermissionsInfo* permissions_info) override; + +DISALLOW_COPY_AND_ASSIGN(ExtensionsAPIProviderQt); +}; + +} + +#endif // EXTENSIONS_API_PROVIDER_QT_H diff --git a/src/core/common/extensions/extensions_client_qt.cpp b/src/core/common/extensions/extensions_client_qt.cpp new file mode 100644 index 000000000..6c6200eb0 --- /dev/null +++ b/src/core/common/extensions/extensions_client_qt.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions_client_qt.h" + +#include "extensions/common/alias.h" +#include "extensions/common/core_extensions_api_provider.h" +#include "extensions/common/extension_urls.h" + +#include "extensions/common/features/simple_feature.h" +#include "extensions/common/permissions/permissions_info.h" + +#include "extensions_api_provider_qt.h" + + +namespace extensions { + +template<class FeatureClass> SimpleFeature *CreateFeature() +{ + return new FeatureClass; +} + +static base::LazyInstance<ExtensionsClientQt>::Leaky g_client = LAZY_INSTANCE_INITIALIZER; + +ExtensionsClientQt *ExtensionsClientQt::GetInstance() +{ + return g_client.Pointer(); +} + +ExtensionsClientQt::ExtensionsClientQt() : ExtensionsClient() +{ + AddAPIProvider(std::make_unique<CoreExtensionsAPIProvider>()); + AddAPIProvider(std::make_unique<ExtensionsAPIProviderQt>()); +} + +// Initializes global state. Not done in the constructor because unit tests +// can create additional ExtensionsClients because the utility thread runs +// in-process. +void ExtensionsClientQt::Initialize() +{ +} + +void ExtensionsClientQt::InitializeWebStoreUrls(base::CommandLine *command_line) +{ +} + +// Returns the global PermissionMessageProvider to use to provide permission +// warning strings. +const PermissionMessageProvider &ExtensionsClientQt::GetPermissionMessageProvider() const +{ + return permission_message_provider_; +} + +// Returns the application name. For example, "Chromium" or "app_shell". +const std::string ExtensionsClientQt::GetProductName() +{ + return "Qt WebEngine"; // return Qt WebEngine for now, consider returning the application name if possible. +} + +// Takes the list of all hosts and filters out those with special +// permission strings. Adds the regular hosts to |new_hosts|, +// and adds any additional permissions to |permissions|. +// TODO(sashab): Split this function in two: One to filter out ignored host +// permissions, and one to get permissions for the given hosts. +void ExtensionsClientQt::FilterHostPermissions(const URLPatternSet &hosts, + URLPatternSet *new_hosts, + PermissionIDSet *permissions) const +{ +} + +// Replaces the scripting whitelist with |whitelist|. Used in the renderer{} +// only used for testing in the browser process. +void ExtensionsClientQt::SetScriptingWhitelist(const ExtensionsClient::ScriptingWhitelist &whitelist) +{ + scripting_whitelist_ = whitelist; +} + +// Return the whitelist of extensions that can run content scripts on +// any origin. +const ExtensionsClient::ScriptingWhitelist &ExtensionsClientQt::GetScriptingWhitelist() const +{ + return scripting_whitelist_; +} + +// Get the set of chrome:// hosts that |extension| can run content scripts on. +URLPatternSet ExtensionsClientQt::GetPermittedChromeSchemeHosts(const Extension *extension, + const APIPermissionSet &api_permissions) const +{ + return URLPatternSet(); +} + +// Returns false if content scripts are forbidden from running on |url|. +bool ExtensionsClientQt::IsScriptableURL(const GURL &url, std::string *error) const +{ + return true; +} + +// Determines if certain fatal extensions errors should be surpressed +// (i.e., only logged) or allowed (i.e., logged before crashing). +bool ExtensionsClientQt::ShouldSuppressFatalErrors() const +{ + return true; +} + +// Records that a fatal error was caught and suppressed. It is expected that +// embedders will only do so if ShouldSuppressFatalErrors at some point +// returned true. +void ExtensionsClientQt::RecordDidSuppressFatalError() +{ +} + +// Returns the base webstore URL prefix. +const GURL &ExtensionsClientQt::GetWebstoreBaseURL() const +{ + if (base_url_.is_empty()) + base_url_ = GURL(extension_urls::kChromeWebstoreBaseURL); + return base_url_; +} + +// Returns the URL to use for update manifest queries. +const GURL &ExtensionsClientQt::GetWebstoreUpdateURL() const +{ + if (update_url_.is_empty()) + update_url_ = GURL(extension_urls::GetWebstoreUpdateUrl()); + return update_url_; +} + +// Returns a flag indicating whether or not a given URL is a valid +// extension blacklist URL. +bool ExtensionsClientQt::IsBlacklistUpdateURL(const GURL &url) const +{ + return true; +} + +// Returns the set of file paths corresponding to any images within an +// extension's contents that may be displayed directly within the browser UI +// or WebUI, such as icons or theme images. This set of paths is used by the +// extension unpacker to determine which assets should be transcoded safely +// within the utility sandbox. +// +// The default implementation returns the images used as icons for the +// extension itself, so implementors of ExtensionsClient overriding this may +// want to call the base class version and then add additional paths to that +// result. +std::set<base::FilePath> ExtensionsClientQt::GetBrowserImagePaths(const Extension *extension) +{ + return ExtensionsClient::GetBrowserImagePaths(extension); +} + +} // namespace extensions diff --git a/src/core/common/extensions/extensions_client_qt.h b/src/core/common/extensions/extensions_client_qt.h new file mode 100644 index 000000000..657487277 --- /dev/null +++ b/src/core/common/extensions/extensions_client_qt.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_CLIENT_QT_H +#define EXTENSIONS_CLIENT_QT_H + +#include "extensions/common/extensions_client.h" + +#include "base/compiler_specific.h" +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "chrome/common/extensions/permissions/chrome_permission_message_provider.h" +#include "extensions/common/features/feature_provider.h" +#include "extensions/common/features/json_feature_provider_source.h" +#include "extensions/common/permissions/extensions_api_permissions.h" +#include "url/gurl.h" + +namespace extensions { + +// Sets up global state for the extensions system. Should be Set() once in each +// process. This should be implemented by the client of the extensions system. +class ExtensionsClientQt : public ExtensionsClient +{ +public: + ExtensionsClientQt(); + virtual ~ExtensionsClientQt() {} + + // Initializes global state. Not done in the constructor because unit tests + // can create additional ExtensionsClients because the utility thread runs + // in-process. + void Initialize() override; + void InitializeWebStoreUrls(base::CommandLine *command_line) override; + + // Returns the global PermissionMessageProvider to use to provide permission + // warning strings. + const PermissionMessageProvider &GetPermissionMessageProvider() const override; + + // Returns the application name. For example, "Chromium" or "app_shell". + const std::string GetProductName() override; + + // Takes the list of all hosts and filters out those with special + // permission strings. Adds the regular hosts to |new_hosts|, + // and adds any additional permissions to |permissions|. + // TODO(sashab): Split this function in two: One to filter out ignored host + // permissions, and one to get permissions for the given hosts. + void FilterHostPermissions(const URLPatternSet &hosts, + URLPatternSet *new_hosts, + PermissionIDSet *permissions) const override; + + // Replaces the scripting whitelist with |whitelist|. Used in the renderer; + // only used for testing in the browser process. + void SetScriptingWhitelist(const ScriptingWhitelist &whitelist) override; + + // Return the whitelist of extensions that can run content scripts on + // any origin. + const ScriptingWhitelist &GetScriptingWhitelist() const override; + + // Get the set of chrome:// hosts that |extension| can run content scripts on. + URLPatternSet GetPermittedChromeSchemeHosts(const Extension *extension, + const APIPermissionSet &api_permissions) const override; + + // Returns false if content scripts are forbidden from running on |url|. + bool IsScriptableURL(const GURL &url, std::string *error) const override; + + // Determines if certain fatal extensions errors should be surpressed + // (i.e., only logged) or allowed (i.e., logged before crashing). + bool ShouldSuppressFatalErrors() const override; + + // Records that a fatal error was caught and suppressed. It is expected that + // embedders will only do so if ShouldSuppressFatalErrors at some point + // returned true. + void RecordDidSuppressFatalError() override; + + // Returns the base webstore URL prefix. + const GURL &GetWebstoreBaseURL() const override; + + // Returns the URL to use for update manifest queries. + const GURL &GetWebstoreUpdateURL() const override; + + // Returns a flag indicating whether or not a given URL is a valid + // extension blacklist URL. + bool IsBlacklistUpdateURL(const GURL &url) const override; + + // Returns the set of file paths corresponding to any images within an + // extension's contents that may be displayed directly within the browser UI + // or WebUI, such as icons or theme images. This set of paths is used by the + // extension unpacker to determine which assets should be transcoded safely + // within the utility sandbox. + // + // The default implementation returns the images used as icons for the + // extension itself, so implementors of ExtensionsClient overriding this may + // want to call the base class version and then add additional paths to that + // result. + std::set<base::FilePath> GetBrowserImagePaths(const Extension *extension) override; + // Get the LazyInstance for ChromeExtensionsClient. + static ExtensionsClientQt *GetInstance(); + +private: + ScriptingWhitelist scripting_whitelist_; + const ChromePermissionMessageProvider permission_message_provider_; + mutable GURL update_url_; + mutable GURL base_url_; + DISALLOW_COPY_AND_ASSIGN(ExtensionsClientQt); +}; + +} // namespace extensions + +#endif // EXTENSIONS_CLIENT_QT_H diff --git a/src/core/chromium_gpu_helper.cpp b/src/core/compositor/chromium_gpu_helper.cpp index 92a8b13ed..71d0f3687 100644 --- a/src/core/chromium_gpu_helper.cpp +++ b/src/core/compositor/chromium_gpu_helper.cpp @@ -43,10 +43,14 @@ #include "chromium_gpu_helper.h" +// Some headers include the namespace ws, and can not coexist with +// Qt headers that include QTextStream, which includes most QSG headers +// via QMatrix4x4. +#include "content/browser/renderer_host/render_widget_host_impl.h" + // Including gpu/command_buffer headers before content/gpu headers makes sure that // guards are defined to prevent duplicate definition errors with forward declared // GL typedefs cascading through content header includes. -#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/texture_base.h" @@ -62,12 +66,6 @@ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner() return content::GpuChildThread::instance()->main_thread_runner(); } -gpu::SyncPointManager *sync_point_manager() -{ - gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); - return gpuChannelManager->sync_point_manager(); -} - gpu::MailboxManager *mailbox_manager() { gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); @@ -85,6 +83,11 @@ unsigned int service_id(gpu::TextureBase *tex) return tex->service_id(); } +void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTicks ¤t_time) +{ + content::RenderWidgetHostImpl::From(host)->ProgressFlingIfNeeded(current_time); +} + #ifdef Q_OS_QNX EGLStreamData eglstream_connect_consumer(gpu::Texture *tex) { diff --git a/src/core/chromium_gpu_helper.h b/src/core/compositor/chromium_gpu_helper.h index 21b764997..4086d12ab 100644 --- a/src/core/chromium_gpu_helper.h +++ b/src/core/compositor/chromium_gpu_helper.h @@ -46,11 +46,15 @@ namespace base { class SingleThreadTaskRunner; +class TimeTicks; +} + +namespace content { +class RenderWidgetHost; } namespace gpu { struct Mailbox; -class SyncPointManager; class MailboxManager; class TextureBase; } @@ -61,12 +65,13 @@ class TextureBase; // functions should only be forward-declared and considered as opaque types. scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner(); -gpu::SyncPointManager *sync_point_manager(); gpu::MailboxManager *mailbox_manager(); gpu::TextureBase* ConsumeTexture(gpu::MailboxManager *mailboxManager, unsigned target, const gpu::Mailbox& mailbox); unsigned int service_id(gpu::TextureBase *tex); +void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTicks ¤t_time); + #ifdef Q_OS_QNX typedef void* EGLDisplay; typedef void* EGLStreamKHR; diff --git a/src/core/compositor.cpp b/src/core/compositor/compositor.cpp index f7a5e651c..0660c155b 100644 --- a/src/core/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -39,26 +39,27 @@ #include "compositor.h" +#include "compositor_resource_tracker.h" #include "delegated_frame_node.h" -#include "render_widget_host_view_qt.h" +#include "base/task/post_task.h" #include "components/viz/common/resources/returned_resource.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" namespace QtWebEngineCore { -Compositor::Compositor(RenderWidgetHostViewQt *hostView) - : m_chromiumCompositorData(new ChromiumCompositorData) - , m_view(hostView) +Compositor::Compositor(content::RenderWidgetHost *host) + : m_resourceTracker(new CompositorResourceTracker) + , m_host(host) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - base::SingleThreadTaskRunner *taskRunner = - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI).get(); + m_taskRunner = base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); m_beginFrameSource = std::make_unique<viz::DelayBasedBeginFrameSource>( - std::make_unique<viz::DelayBasedTimeSource>(taskRunner), + std::make_unique<viz::DelayBasedTimeSource>(m_taskRunner.get()), viz::BeginFrameSource::kNotRestartableId); } @@ -67,13 +68,6 @@ Compositor::~Compositor() DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void Compositor::setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - m_viewDelegate = viewDelegate; -} - void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -85,7 +79,7 @@ void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frame // should not be returned. // // TODO(juvaldma): Can there be a pending frame from the old client? - m_resourcesToRelease.clear(); + m_resourceTracker->returnResources(); m_frameSinkClient = frameSinkClient; } @@ -104,21 +98,19 @@ void Compositor::setNeedsBeginFrames(bool needsBeginFrames) m_needsBeginFrames = needsBeginFrames; } -void Compositor::submitFrame(viz::CompositorFrame frame) +void Compositor::submitFrame(viz::CompositorFrame frame, base::OnceClosure callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!m_havePendingFrame); - - m_chromiumCompositorData->frameDevicePixelRatio = frame.metadata.device_scale_factor; - m_chromiumCompositorData->previousFrameData = std::move(m_chromiumCompositorData->frameData); - m_chromiumCompositorData->frameData = std::move(frame); - m_havePendingFrame = true; + DCHECK(!m_submitCallback); - // Tell viewDelegate to call updatePaintNode() soon. - m_viewDelegate->update(); + m_pendingFrame = std::move(frame); + m_submitCallback = std::move(callback); + m_resourceTracker->submitResources( + m_pendingFrame, + base::BindOnce(&Compositor::runSubmitCallback, base::Unretained(this))); } -QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) +QSGNode *Compositor::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate) { // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // @@ -129,30 +121,42 @@ QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) if (!frameNode) frameNode = new DelegatedFrameNode; - frameNode->commit(m_chromiumCompositorData.data(), &m_resourcesToRelease, m_viewDelegate); - - if (m_havePendingFrame) { - m_havePendingFrame = false; - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); + if (!m_updatePaintNodeShouldCommit) { + frameNode->commit(m_committedFrame, viz::CompositorFrame(), m_resourceTracker.get(), viewDelegate); + return frameNode; } - if (m_chromiumCompositorData->frameData.metadata.request_presentation_feedback) - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_chromiumCompositorData->frameData.metadata.frame_token)); + m_updatePaintNodeShouldCommit = false; + + if (m_committedFrame.metadata.request_presentation_feedback) + m_taskRunner->PostTask(FROM_HERE, + base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), + m_committedFrame.metadata.frame_token)); + m_resourceTracker->commitResources(); + frameNode->commit(m_pendingFrame, m_committedFrame, m_resourceTracker.get(), viewDelegate); + m_committedFrame = std::move(m_pendingFrame); + m_pendingFrame = viz::CompositorFrame(); + + m_taskRunner->PostTask(FROM_HERE, + base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); return frameNode; } +void Compositor::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + m_updatePaintNodeShouldCommit = true; + std::move(m_submitCallback).Run(); +} + void Compositor::notifyFrameCommitted() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); m_beginFrameSource->DidFinishFrame(this); if (m_frameSinkClient) - m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourcesToRelease); - m_resourcesToRelease.clear(); + m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourceTracker->returnResources()); } void Compositor::sendPresentationFeedback(uint frame_token) @@ -165,7 +169,7 @@ bool Compositor::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - m_view->OnBeginFrame(args.frame_time); + ProgressFlingIfNeeded(m_host, args.frame_time); m_beginFrameSource->OnUpdateVSyncParameters(args.frame_time, args.interval); if (m_frameSinkClient) m_frameSinkClient->OnBeginFrame(args); diff --git a/src/core/compositor.h b/src/core/compositor/compositor.h index 7d7db5d04..69f4a530c 100644 --- a/src/core/compositor.h +++ b/src/core/compositor/compositor.h @@ -42,6 +42,10 @@ #include <base/memory/weak_ptr.h> #include <components/viz/common/frame_sinks/begin_frame_source.h> +#include <components/viz/common/quads/compositor_frame.h> + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> @@ -50,8 +54,10 @@ QT_BEGIN_NAMESPACE class QSGNode; QT_END_NAMESPACE +namespace content { +class RenderWidgetHost; +} namespace viz { -class CompositorFrame; struct ReturnedResource; namespace mojom { class CompositorFrameSinkClient; @@ -60,9 +66,8 @@ class CompositorFrameSinkClient; namespace QtWebEngineCore { -class RenderWidgetHostViewQt; +class CompositorResourceTracker; class RenderWidgetHostViewQtDelegate; -class ChromiumCompositorData; // Receives viz::CompositorFrames from child compositors and provides QSGNodes // to the Qt Quick renderer. @@ -72,10 +77,10 @@ class ChromiumCompositorData; // Step 1. A new CompositorFrame is received from child compositors and handed // off to submitFrame(). The new frame will start off in a pending state. // -// Step 2. Once the new frame is ready to be rendered, Compositor will call -// update() on the delegate. +// Step 2. Once the new frame is ready to be rendered, Compositor will notify +// the client by running the callback given to submitFrame(). // -// Step 3. Once the delegate is ready to render, updatePaintNode() should be +// Step 3. Once the client is ready to render, updatePaintNode() should be // called to receive the scene graph for the new frame. This call will commit // the pending frame. Until the next frame is ready, all subsequent calls to // updatePaintNode() will keep using this same committed frame. @@ -85,18 +90,17 @@ class ChromiumCompositorData; class Compositor final : private viz::BeginFrameObserverBase { public: - explicit Compositor(RenderWidgetHostViewQt *hostView); + explicit Compositor(content::RenderWidgetHost *host); ~Compositor() override; - void setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate); void setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient); void setNeedsBeginFrames(bool needsBeginFrames); - void submitFrame(viz::CompositorFrame frame); - - QSGNode *updatePaintNode(QSGNode *oldNode); + void submitFrame(viz::CompositorFrame frame, base::OnceClosure callback); + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate); private: + void runSubmitCallback(); void notifyFrameCommitted(); void sendPresentationFeedback(uint frame_token); @@ -104,15 +108,17 @@ private: bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) override; void OnBeginFrameSourcePausedChanged(bool paused) override; - std::vector<viz::ReturnedResource> m_resourcesToRelease; - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; - RenderWidgetHostViewQt *m_view; - RenderWidgetHostViewQtDelegate *m_viewDelegate = nullptr; + viz::CompositorFrame m_committedFrame; + viz::CompositorFrame m_pendingFrame; + base::OnceClosure m_submitCallback; + std::unique_ptr<CompositorResourceTracker> m_resourceTracker; + content::RenderWidgetHost *m_host; std::unique_ptr<viz::SyntheticBeginFrameSource> m_beginFrameSource; viz::mojom::CompositorFrameSinkClient *m_frameSinkClient = nullptr; - bool m_havePendingFrame = false; + bool m_updatePaintNodeShouldCommit = false; bool m_needsBeginFrames = false; + scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; base::WeakPtrFactory<Compositor> m_weakPtrFactory{this}; DISALLOW_COPY_AND_ASSIGN(Compositor); diff --git a/src/core/compositor/compositor_resource.h b/src/core/compositor/compositor_resource.h new file mode 100644 index 000000000..f7df2ab59 --- /dev/null +++ b/src/core/compositor/compositor_resource.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_H +#define COMPOSITOR_RESOURCE_H + +#include <base/memory/ref_counted.h> +#include <components/viz/common/resources/transferable_resource.h> + +#include <QtCore/qglobal.h> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +# include "compositor_resource_fence.h" +#endif + +namespace viz { +class SharedBitmap; +} // namespace viz + +namespace QtWebEngineCore { + +using CompositorResourceId = quint32; + +// A resource (OpenGL texture or software shared bitmap). +// +// - Created by the CompositorResourceTracker from a newly submitted +// CompositorFrame's resource_list. +// +// - Until the frame is committed, its resources are in a 'pending' state and +// are inaccessible from outside the CompositorResourceTracker. +// +// - Once the frame is committed, its resources can be found via +// CompositorResourceTracker::findResource. +// +// - A committed resource's fields may not be updated and are safe to use from +// other threads without synchronization (unless noted otherwise). +class CompositorResource : public viz::TransferableResource +{ +public: + CompositorResource(const viz::TransferableResource &tr) : viz::TransferableResource(tr) {} + + // Counts the number of times this resource has been encountered in + // CompositorFrames' resource lists. + // + // Corresponds to viz::ReturnedResource::count. + // + // Updated by CompositorResourceTracker on UI thread. + int import_count = 1; + + // Identifies the last frame that needed this resource. Used by + // CompositorResourceTracker to return unused resources back to child + // compositors. + // + // Updated by CompositorResourceTracker on UI thread. + quint32 last_used_for_frame = 0; + + // Bitmap (if is_software). + std::unique_ptr<viz::SharedBitmap> bitmap; + +#if QT_CONFIG(opengl) + // OpenGL texture id (if !is_software). + quint32 texture_id = 0; + + // Should be waited on before using the texture (non-null if !is_software). + scoped_refptr<CompositorResourceFence> texture_fence; +#endif // QT_CONFIG(opengl) +}; + +inline bool operator<(const CompositorResource &r1, const CompositorResource &r2) +{ + return r1.id < r2.id; +} + +inline bool operator<(const CompositorResource &r, CompositorResourceId id) +{ + return r.id < id; +} + +inline bool operator<(CompositorResourceId id, const CompositorResource &r) +{ + return id < r.id; +} + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_H diff --git a/src/core/compositor/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp new file mode 100644 index 000000000..7fc5fbfb2 --- /dev/null +++ b/src/core/compositor/compositor_resource_fence.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_fence.h" + +#include "ui/gl/gl_context.h" + +#include <QtGui/qopenglcontext.h> + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +namespace QtWebEngineCore { + +void CompositorResourceFence::wait() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + Q_ASSERT(context); + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); + resolved = true; + } + + if (eglClientWaitSyncKHR) + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglClientWaitSyncKHR(m_sync.egl.display, m_sync.egl.sync, 0, EGL_FOREVER_KHR); + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); + static WaitSyncPtr glWaitSync_ = 0; + if (!glWaitSync_) { + glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); + Q_ASSERT(glWaitSync_); + } + glWaitSync_(m_sync.arb.sync, 0, GL_TIMEOUT_IGNORED); + break; + } + + release(); +} + +void CompositorResourceFence::release() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) + return; + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); + resolved = true; + } + + if (eglDestroySyncKHR) { + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglDestroySyncKHR(m_sync.egl.display, m_sync.egl.sync); + m_sync.reset(); + } + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); + static DeleteSyncPtr glDeleteSync_ = 0; + if (!glDeleteSync_) { + glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); + Q_ASSERT(glDeleteSync_); + } + glDeleteSync_(m_sync.arb.sync); + m_sync.reset(); + break; + } + // If Chromium was able to create a sync, we should have been able to handle its type here too. + Q_ASSERT(!m_sync); +} + +// static +scoped_refptr<CompositorResourceFence> CompositorResourceFence::create() +{ + if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { + std::unique_ptr<gl::GLFence> glFence{gl::GLFence::Create()}; + return base::MakeRefCounted<CompositorResourceFence>(glFence->Transfer()); + } + return nullptr; +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/url_request_qrc_job_qt.h b/src/core/compositor/compositor_resource_fence.h index 11c130693..1c2ea3695 100644 --- a/src/core/net/url_request_qrc_job_qt.h +++ b/src/core/compositor/compositor_resource_fence.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,40 +37,35 @@ ** ****************************************************************************/ -#ifndef URL_REQUEST_QRC_JOB_QT_H_ -#define URL_REQUEST_QRC_JOB_QT_H_ +#ifndef COMPOSITOR_RESOURCE_FENCE_H +#define COMPOSITOR_RESOURCE_FENCE_H -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_job.h" - -#include <QFile> +#include <base/memory/ref_counted.h> +#include <ui/gl/gl_fence.h> namespace QtWebEngineCore { -// A request job that handles reading qrc file URLs -class URLRequestQrcJobQt : public net::URLRequestJob { - +// Sync object created on GPU thread and consumed on render thread. +class CompositorResourceFence final : public base::RefCountedThreadSafe<CompositorResourceFence> +{ public: - URLRequestQrcJobQt(net::URLRequest *request, net::NetworkDelegate *networkDelegate); - void Start() override; - void Kill() override; - int ReadRawData(net::IOBuffer* buf, int buf_size) override;; - bool GetMimeType(std::string *mimeType) const override; + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); -protected: - virtual ~URLRequestQrcJobQt(); - // Get file mime type and try open file on a background thread. - void startGetHead(); + CompositorResourceFence() {} + CompositorResourceFence(const gl::TransferableFence &sync) : m_sync(sync) {}; + ~CompositorResourceFence() { release(); } -private: - qint64 m_remainingBytes; - QFile m_file; - std::string m_mimeType; - base::WeakPtrFactory<URLRequestQrcJobQt> m_weakFactory; + // May be used only by Qt Quick render thread. + void wait(); + void release(); - DISALLOW_COPY_AND_ASSIGN(URLRequestQrcJobQt); + // May be used only by GPU thread. + static scoped_refptr<CompositorResourceFence> create(); + +private: + gl::TransferableFence m_sync; }; } // namespace QtWebEngineCore -#endif // URL_REQUEST_QRC_JOB_QT_H_ +#endif // !COMPOSITOR_RESOURCE_FENCE_H diff --git a/src/core/compositor/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp new file mode 100644 index 000000000..6530f3249 --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_tracker.h" + +#include "chromium_gpu_helper.h" +#include "render_widget_host_view_qt_delegate.h" +#include "web_engine_context.h" + +#include "base/message_loop/message_loop.h" +#include "base/task/post_task.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/resources/returned_resource.h" +#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" +#include "content/browser/browser_main_loop.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/gpu/content_gpu_client.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/sync_point_manager.h" + +namespace QtWebEngineCore { + +CompositorResourceTracker::CompositorResourceTracker() +{} + +CompositorResourceTracker::~CompositorResourceTracker() +{} + +void CompositorResourceTracker::submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!m_submitCallback); + DCHECK(m_pendingResources.empty()); + DCHECK(m_pendingImports.empty()); + DCHECK(m_pendingResourceUpdates == 0); + + m_submitCallback = std::move(callback); + + m_pendingResources.reserve(frame.resource_list.size()); + m_pendingImports.reserve(frame.resource_list.size()); + + for (const viz::TransferableResource &transferableResource : frame.resource_list) { + auto it = m_committedResources.find(transferableResource.id); + if (it != m_committedResources.end()) + m_pendingImports.push_back(&*it); + else + m_pendingResources.emplace_back(transferableResource); + } + + if (m_pendingResources.empty()) { + scheduleRunSubmitCallback(); + return; + } + + m_pendingResourceUpdates = m_pendingResources.size(); + + std::vector<CompositorResource *> batch; + batch.reserve(m_pendingResources.size()); + + for (CompositorResource &resource : m_pendingResources) { + if (resource.is_software) + updateBitmap(&resource); + else if (!scheduleUpdateMailbox(&resource)) + batch.push_back(&resource); + } + + if (!batch.empty()) + scheduleUpdateMailboxes(std::move(batch)); +} + +void CompositorResourceTracker::commitResources() +{ + // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // + // This might be called from a Qt Quick render thread, but the UI thread + // will still be blocked for the duration of this call. + + DCHECK(m_pendingResourceUpdates == 0); + + for (CompositorResource *resource : m_pendingImports) + resource->import_count++; + m_pendingImports.clear(); + + m_committedResources.insert(std::make_move_iterator(m_pendingResources.begin()), + std::make_move_iterator(m_pendingResources.end())); + m_pendingResources.clear(); + + ++m_committedFrameId; +} + +std::vector<viz::ReturnedResource> CompositorResourceTracker::returnResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::vector<viz::ReturnedResource> returnedResources; + base::EraseIf(m_committedResources, [&](const CompositorResource &resource) { + if (resource.last_used_for_frame != m_committedFrameId) { + viz::ReturnedResource returnedResource; + returnedResource.id = resource.id; + returnedResource.count = resource.import_count; + returnedResources.push_back(std::move(returnedResource)); + return true; + } + return false; + }); + return returnedResources; +} + +const CompositorResource *CompositorResourceTracker::findResource(CompositorResourceId id) const +{ + auto it = m_committedResources.find(id); + DCHECK(it != m_committedResources.end()); + + const_cast<CompositorResource &>(*it).last_used_for_frame = m_committedFrameId; + + return &*it; +} + +void CompositorResourceTracker::updateBitmap(CompositorResource *resource) +{ + content::BrowserMainLoop *browserMainLoop = content::BrowserMainLoop::GetInstance(); + viz::ServerSharedBitmapManager *bitmapManager = browserMainLoop->GetServerSharedBitmapManager(); + + resource->bitmap = bitmapManager->GetSharedBitmapFromId( + resource->size, + viz::BGRA_8888, + resource->mailbox_holder.mailbox); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +} + +quint32 CompositorResourceTracker::consumeMailbox(const gpu::MailboxHolder &mailboxHolder) +{ +#if QT_CONFIG(opengl) + gpu::MailboxManager *mailboxManager = mailbox_manager(); + DCHECK(mailboxManager); + if (mailboxHolder.sync_token.HasData()) + mailboxManager->PullTextureUpdates(mailboxHolder.sync_token); + return service_id(mailboxManager->ConsumeTexture(mailboxHolder.mailbox)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +bool CompositorResourceTracker::scheduleUpdateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + gpu::SyncPointManager *syncPointManager = WebEngineContext::syncPointManager(); + DCHECK(syncPointManager); + return syncPointManager->WaitOutOfOrder( + resource->mailbox_holder.sync_token, + base::BindOnce(&CompositorResourceTracker::updateMailbox, + m_weakPtrFactory.GetWeakPtr(), + resource)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + resource->texture_fence = CompositorResourceFence::create(); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleUpdateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); + DCHECK(gpuTaskRunner); + gpuTaskRunner->PostTask( + FROM_HERE, + base::BindOnce(&CompositorResourceTracker::updateMailboxes, + m_weakPtrFactory.GetWeakPtr(), + std::move(resources))); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + for (CompositorResource *resource : resources) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + + scoped_refptr<CompositorResourceFence> fence = CompositorResourceFence::create(); + + for (CompositorResource *resource : resources) + resource->texture_fence = fence; + + if ((m_pendingResourceUpdates -= resources.size()) == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleRunSubmitCallback() +{ + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&CompositorResourceTracker::runSubmitCallback, + m_weakPtrFactory.GetWeakPtr())); +} + +void CompositorResourceTracker::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::move(m_submitCallback).Run(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/compositor_resource_tracker.h b/src/core/compositor/compositor_resource_tracker.h new file mode 100644 index 000000000..887309395 --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_TRACKER_H +#define COMPOSITOR_RESOURCE_TRACKER_H + +#include "compositor_resource.h" +#include "locked_ptr.h" + +#include <base/callback.h> +#include <base/containers/flat_set.h> + +#include <atomic> +#include <vector> + +namespace viz { +class CompositorFrame; +struct ReturnedResource; +} // namespace viz + +namespace gpu { +struct MailboxHolder; +} // namespace gpu + +namespace QtWebEngineCore { + +// Ensures resources are not used before they are ready. +// +// The life cycle of a frame's resources: +// +// Step 1. A new CompositorFrame is received and given to submitResources(). +// The frame's resources will extracted and initialized to a pending state. +// +// Step 2. Once the new resources are ready to be committed, +// CompositorResourceTracker will notify the client by running the callback +// given to submitResources(). +// +// Step 3. Once the client is ready to render, commitResources() should be +// called. This will commit all the pending resources, making them available +// via findResource(). +// +// Step 4. Once all the resources have been used (via findResource()), +// returnResources() may be called to return a list of all the resources which +// were *not* used since the last commitResources(). Go to step 1. +class CompositorResourceTracker final +{ +public: + CompositorResourceTracker(); + ~CompositorResourceTracker(); + + void submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback); + void commitResources(); + std::vector<viz::ReturnedResource> returnResources(); + + // The returned pointer is invalidated by the next call to commitFrame() or + // returnResources(). It should therefore not be stored in data structures + // but used immediately. + // + // Do not ask for resources which do not exist. + const CompositorResource *findResource(CompositorResourceId id) const; + +private: + void updateBitmap(CompositorResource *resource); + + quint32 consumeMailbox(const gpu::MailboxHolder &mailboxHolder); + + bool scheduleUpdateMailbox(CompositorResource *resource); + void updateMailbox(CompositorResource *resource); + + void scheduleUpdateMailboxes(std::vector<CompositorResource *> resources); + void updateMailboxes(std::vector<CompositorResource *> resources); + + void scheduleRunSubmitCallback(); + void runSubmitCallback(); + + base::flat_set<CompositorResource> m_committedResources; + std::vector<CompositorResource> m_pendingResources; + std::vector<CompositorResource *> m_pendingImports; + base::OnceClosure m_submitCallback; + std::atomic<size_t> m_pendingResourceUpdates{0}; + quint32 m_committedFrameId = 0; + + base::LockedPtrFactory<CompositorResourceTracker> m_weakPtrFactory{this}; + + DISALLOW_COPY_AND_ASSIGN(CompositorResourceTracker); +}; + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_TRACKER_H diff --git a/src/core/compositor/content_gpu_client_qt.cpp b/src/core/compositor/content_gpu_client_qt.cpp new file mode 100644 index 000000000..f934979a0 --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "content_gpu_client_qt.h" + +#include "web_engine_context.h" + +namespace QtWebEngineCore { + +ContentGpuClientQt::ContentGpuClientQt() +{ +} + +ContentGpuClientQt::~ContentGpuClientQt() +{ +} + +gpu::SyncPointManager *ContentGpuClientQt::GetSyncPointManager() +{ + return WebEngineContext::syncPointManager(); +} + +} // namespace diff --git a/src/core/compositor/content_gpu_client_qt.h b/src/core/compositor/content_gpu_client_qt.h new file mode 100644 index 000000000..d7ad43881 --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONTENT_GPU_CLIENT_QT_H +#define CONTENT_GPU_CLIENT_QT_H + +#include "content/public/gpu/content_gpu_client.h" + +namespace QtWebEngineCore { + +class ContentGpuClientQt : public content::ContentGpuClient { +public: + explicit ContentGpuClientQt(); + ~ContentGpuClientQt() override; + + // content::ContentGpuClient implementation. + gpu::SyncPointManager *GetSyncPointManager() override; +}; + +} + +#endif // CONTENT_GPU_CLIENT_QT_H diff --git a/src/core/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index 8ac82dbf1..4fb05051b 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -49,17 +49,14 @@ #include "delegated_frame_node.h" #include "chromium_gpu_helper.h" -#include "ozone/gl_surface_qt.h" #include "stream_video_node.h" #include "type_conversion.h" #include "yuv_video_node.h" +#include "compositor_resource_tracker.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_task_runner_handle.h" #include "cc/base/math_util.h" #include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/render_pass_draw_quad.h" @@ -68,14 +65,8 @@ #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/bsp_tree.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "content/browser/browser_main_loop.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_fence.h" #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -93,10 +84,6 @@ #include <EGL/eglext.h> #endif -#ifndef GL_TIMEOUT_IGNORED -#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull -#endif - #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif @@ -131,7 +118,7 @@ namespace QtWebEngineCore { #ifndef QT_NO_OPENGL class MailboxTexture : public QSGTexture, protected QOpenGLFunctions { public: - MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize); + MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1); ~MailboxTexture(); // QSGTexture: int textureId() const override { return m_textureId; } @@ -140,14 +127,9 @@ public: bool hasMipmaps() const override { return false; } void bind() override; - void setHasAlphaChannel(bool hasAlpha) { m_hasAlpha = hasAlpha; } - gpu::MailboxHolder &mailboxHolder() { return m_mailboxHolder; } - void fetchTexture(gpu::MailboxManager *mailboxManager); - void setTarget(GLenum target); - private: - gpu::MailboxHolder m_mailboxHolder; int m_textureId; + scoped_refptr<CompositorResourceFence> m_fence; QSize m_textureSize; bool m_hasAlpha; GLenum m_target; @@ -160,20 +142,6 @@ private: friend class DelegatedFrameNode; }; #endif // QT_NO_OPENGL -class ResourceHolder { -public: - ResourceHolder(const viz::TransferableResource &resource); - QSharedPointer<QSGTexture> initTexture(bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - QSGTexture *texture() const { return m_texture.data(); } - viz::ReturnedResource returnResource(); - void incImportCount() { ++m_importCount; } - bool needsToFetch() const { return !m_resource.is_software && m_texture && !m_texture.data()->textureId(); } - -private: - QWeakPointer<QSGTexture> m_texture; - viz::TransferableResource m_resource; - int m_importCount; -}; class RectClipNode : public QSGClipNode { @@ -443,7 +411,9 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } if (!layerState->quad_to_target_transform.IsIdentity()) { QSGTransformNode *transformNode = new QSGTransformNode; - transformNode->setMatrix(toQt(layerState->quad_to_target_transform.matrix())); + QMatrix4x4 qMatrix; + convertToQt(layerState->quad_to_target_transform.matrix(), qMatrix); + transformNode->setMatrix(qMatrix); layerChain->appendChildNode(transformNode); layerChain = transformNode; } @@ -457,99 +427,12 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } #ifndef QT_NO_OPENGL -static void waitChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); - } - resolved = true; - } - - if (eglClientWaitSyncKHR) - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglClientWaitSyncKHR(sync->egl.display, sync->egl.sync, 0, EGL_FOREVER_KHR); - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); - static WaitSyncPtr glWaitSync_ = 0; - if (!glWaitSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); - Q_ASSERT(glWaitSync_); - } - glWaitSync_(sync->arb.sync, 0, GL_TIMEOUT_IGNORED); - break; - } -} - -static void deleteChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); - } - resolved = true; - } - - if (eglDestroySyncKHR) { - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglDestroySyncKHR(sync->egl.display, sync->egl.sync); - sync->reset(); - } - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); - static DeleteSyncPtr glDeleteSync_ = 0; - if (!glDeleteSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); - Q_ASSERT(glDeleteSync_); - } - glDeleteSync_(sync->arb.sync); - sync->reset(); - break; - } - // If Chromium was able to create a sync, we should have been able to handle its type here too. - Q_ASSERT(!*sync); -} - -MailboxTexture::MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize) - : m_mailboxHolder(mailboxHolder) - , m_textureId(0) - , m_textureSize(textureSize) - , m_hasAlpha(false) - , m_target(GL_TEXTURE_2D) +MailboxTexture::MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) + : m_textureId(resource->texture_id) + , m_fence(resource->texture_fence) + , m_textureSize(toQt(resource->size)) + , m_hasAlpha(hasAlphaChannel) + , m_target(target >= 0 ? target : GL_TEXTURE_2D) #if defined(USE_OZONE) , m_ownsTexture(false) #endif @@ -580,6 +463,8 @@ MailboxTexture::~MailboxTexture() void MailboxTexture::bind() { + if (m_fence) + m_fence->wait(); glBindTexture(m_target, m_textureId); #ifdef Q_OS_QNX if (m_target == GL_TEXTURE_EXTERNAL_OES) { @@ -596,96 +481,7 @@ void MailboxTexture::bind() } #endif } - -void MailboxTexture::setTarget(GLenum target) -{ - m_target = target; -} - -void MailboxTexture::fetchTexture(gpu::MailboxManager *mailboxManager) -{ - gpu::TextureBase *tex = ConsumeTexture(mailboxManager, m_target, m_mailboxHolder.mailbox); - - // The texture might already have been deleted (e.g. when navigating away from a page). - if (tex) { - m_textureId = service_id(tex); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - m_eglStreamData = eglstream_connect_consumer(tex); - } -#endif - } -} -#endif //QT_NO_OPENGL - -ResourceHolder::ResourceHolder(const viz::TransferableResource &resource) - : m_resource(resource) - , m_importCount(1) -{ -} - -QSharedPointer<QSGTexture> ResourceHolder::initTexture(bool quadNeedsBlending, RenderWidgetHostViewQtDelegate *apiDelegate) -{ - QSharedPointer<QSGTexture> texture = m_texture.toStrongRef(); - if (!texture) { - if (m_resource.is_software) { - Q_ASSERT(apiDelegate); - std::unique_ptr<viz::SharedBitmap> sharedBitmap = - content::BrowserMainLoop::GetInstance()->GetServerSharedBitmapManager()->GetSharedBitmapFromId( - m_resource.size, viz::BGRA_8888, m_resource.mailbox_holder.mailbox); - // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending - // to draw it but Chromium keeps this information in the quads. - // The input format is currently always Format_ARGB32_Premultiplied, so assume that all - // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion - // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to - // return false. - QImage::Format format = quadNeedsBlending ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - QImage image = sharedBitmap - ? QImage(sharedBitmap->pixels(), m_resource.size.width(), m_resource.size.height(), format) - : QImage(m_resource.size.width(), m_resource.size.height(), format); - texture.reset(apiDelegate->createTextureFromImage(image.copy())); - } else { -#ifndef QT_NO_OPENGL - texture.reset(new MailboxTexture(m_resource.mailbox_holder, toQt(m_resource.size))); - static_cast<MailboxTexture *>(texture.data())->setHasAlphaChannel(quadNeedsBlending); -#else - Q_UNREACHABLE(); -#endif - } - if (m_resource.filter == GL_NEAREST) - texture->setFiltering(QSGTexture::Nearest); - else if (m_resource.filter == GL_LINEAR) - texture->setFiltering(QSGTexture::Linear); - else { - // Depends on qtdeclarative fix, see QTBUG-71322 -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) - texture->setFiltering(QSGTexture::Linear); -#else - texture->setFiltering(QSGTexture::Nearest); -#endif - } - m_texture = texture; - } - // All quads using a resource should request the same blending state. - Q_ASSERT(texture->hasAlphaChannel() || !quadNeedsBlending); - return texture; -} - -viz::ReturnedResource ResourceHolder::returnResource() -{ - viz::ReturnedResource returned; - // The ResourceProvider ensures that the resource isn't used by the parent compositor's GL - // context in the GPU process by inserting a sync point to be waited for by the child - // compositor's GL context. We don't need this since we are triggering the delegated frame - // ack directly from our rendering thread. At this point (in updatePaintNode) we know that - // a frame that was compositing any of those resources has already been swapped and we thus - // don't need to use this mechanism. - returned.id = m_resource.id; - returned.count = m_importCount; - m_importCount = 0; - return returned; -} - +#endif // !QT_NO_OPENGL RectClipNode::RectClipNode(const QRectF &rect) : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) @@ -697,9 +493,8 @@ RectClipNode::RectClipNode(const QRectF &rect) } DelegatedFrameNode::DelegatedFrameNode() - : m_numPendingSyncPoints(0) #if defined(USE_OZONE) && !defined(QT_NO_OPENGL) - , m_contextShared(true) + : m_contextShared(true) #endif { setFlag(UsePreprocess); @@ -725,22 +520,6 @@ DelegatedFrameNode::~DelegatedFrameNode() void DelegatedFrameNode::preprocess() { -#ifndef QT_NO_OPENGL - // With the threaded render loop the GUI thread has been unlocked at this point. - // We can now wait for the Chromium GPU thread to produce textures that will be - // rendered on our quads and fetch the IDs from the mailboxes we were given. - QList<MailboxTexture *> mailboxesToFetch; - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; - ResourceHolderIterator end = m_chromiumCompositorData->resourceHolders.constEnd(); - for (ResourceHolderIterator it = m_chromiumCompositorData->resourceHolders.constBegin(); it != end ; ++it) { - if ((*it)->needsToFetch()) - mailboxesToFetch.append(static_cast<MailboxTexture *>((*it)->texture())); - } - - if (!mailboxesToFetch.isEmpty()) - fetchAndSyncMailboxes(mailboxesToFetch); -#endif - // Then render any intermediate RenderPass in order. typedef QPair<int, QSharedPointer<QSGLayer> > Pair; for (const Pair &pair : qAsConst(m_sgObjects.renderPassLayers)) { @@ -767,8 +546,8 @@ static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState, // Compares if the frame data that we got from the Chromium Compositor is // *structurally* equivalent to the one of the previous frame. // If it is, we will just reuse and update the old nodes where necessary. -static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, - viz::CompositorFrame *previousFrameData) +static bool areRenderPassStructuresEqual(const viz::CompositorFrame *frameData, + const viz::CompositorFrame *previousFrameData) { if (!previousFrameData) return false; @@ -820,12 +599,12 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, return true; } -void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, - std::vector<viz::ReturnedResource> *resourcesToRelease, +void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame, + const viz::CompositorFrame &committedFrame, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { - m_chromiumCompositorData = chromiumCompositorData; - viz::CompositorFrame* frameData = &m_chromiumCompositorData->frameData; + const viz::CompositorFrame* frameData = &pendingFrame; if (frameData->render_pass_list.empty()) return; @@ -833,30 +612,14 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - const float devicePixelRatio = m_chromiumCompositorData->frameDevicePixelRatio; + const float devicePixelRatio = frameData->metadata.device_scale_factor; matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); if (QSGTransformNode::matrix() != matrix) setMatrix(matrix); - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceCandidates; - qSwap(m_chromiumCompositorData->resourceHolders, resourceCandidates); - - // A frame's resource_list only contains the new resources to be added to the scene. Quads can - // still reference resources that were added in previous frames. Add them to the list of - // candidates to be picked up by quads, it's then our responsibility to return unused resources - // to the producing child compositor. - for (unsigned i = 0; i < frameData->resource_list.size(); ++i) { - const viz::TransferableResource &res = frameData->resource_list.at(i); - if (QSharedPointer<ResourceHolder> resource = resourceCandidates.value(res.id)) - resource->incImportCount(); - else - resourceCandidates[res.id] = QSharedPointer<ResourceHolder>(new ResourceHolder(res)); - } - - frameData->resource_list.clear(); QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; - const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); + const QSizeF viewportSizeInPt = apiDelegate->viewGeometry().size(); const QSizeF viewportSizeF = viewportSizeInPt * devicePixelRatio; const QSize viewportSize(std::ceil(viewportSizeF.width()), std::ceil(viewportSizeF.height())); @@ -867,26 +630,22 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside // of the visible area, we also have to rebuild the tree whenever the window is resized. const bool buildNewTree = - !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || + !areRenderPassStructuresEqual(frameData, &committedFrame) || m_sceneGraphNodes.empty() || viewportSize != m_previousViewportSize; - m_chromiumCompositorData->previousFrameData = viz::CompositorFrame(); - SGObjects previousSGObjects; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; if (buildNewTree) { // Keep the old objects in scope to hold a ref on layers, resources and textures // that we can re-use. Destroy the remaining objects before returning. - qSwap(m_sgObjects, previousSGObjects); + qSwap(m_sgObjects, m_previousSGObjects); // Discard the scene graph nodes from the previous frame. while (QSGNode *oldChain = firstChild()) delete oldChain; m_sceneGraphNodes.clear(); nodeHandler.reset(new DelegatedNodeTreeCreator(&m_sceneGraphNodes, apiDelegate)); } else { - // Save the texture strong refs so they only go out of scope when the method returns and - // the new vector of texture strong refs has been filled. - qSwap(m_sgObjects.textureStrongRefs, textureStrongRefs); + qSwap(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures); + qSwap(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures); nodeHandler.reset(new DelegatedNodeTreeUpdater(&m_sceneGraphNodes)); } // The RenderPasses list is actually a tree where a parent RenderPass is connected @@ -906,7 +665,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, if (pass != rootRenderPass) { QSharedPointer<QSGLayer> rpLayer; if (buildNewTree) { - rpLayer = findRenderPassLayer(pass->id, previousSGObjects.renderPassLayers); + rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers); if (!rpLayer) { rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer()); // Avoid any premature texture update since we need to wait @@ -935,7 +694,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } if (scissorRect.IsEmpty()) { - holdResources(pass, resourceCandidates); + holdResources(pass, resourceTracker); continue; } @@ -961,13 +720,13 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, targetRect.Intersect(quadState->clip_rect); targetRect.Intersect(scissorRect); if (targetRect.IsEmpty()) { - holdResources(quad, resourceCandidates); + holdResources(quad, resourceTracker); continue; } if (quadState->sorting_context_id != currentSortingContextId) { flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); currentSortingContextId = quadState->sorting_context_id; } @@ -989,28 +748,23 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } handleQuad(quad, currentLayerChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } - // Send resources of remaining candidates back to the child compositors so that - // they can be freed or reused. - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator - ResourceHolderIterator; - ResourceHolderIterator end = resourceCandidates.constEnd(); - for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) - resourcesToRelease->push_back((*it)->returnResource()); + copyMailboxTextures(); m_previousViewportSize = viewportSize; + m_previousSGObjects = SGObjects(); } void DelegatedFrameNode::flushPolygons( base::circular_deque<std::unique_ptr<viz::DrawPolygon>> *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (polygonQueue->empty()) @@ -1030,7 +784,7 @@ void DelegatedFrameNode::flushPolygons( polygon->TransformToLayerSpace(inverseTransform); handlePolygon(polygon, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); }; viz::BspTree(polygonQueue).TraverseWithActionHandler(&actionHandler); @@ -1040,20 +794,20 @@ void DelegatedFrameNode::handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { const viz::DrawQuad *quad = polygon->original_ref(); if (!polygon->is_split()) { handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } else { std::vector<gfx::QuadF> clipRegionList; polygon->ToQuads2D(&clipRegionList); for (const auto & clipRegion : clipRegionList) handleClippedQuad(quad, clipRegion, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } } @@ -1062,7 +816,7 @@ void DelegatedFrameNode::handleClippedQuad( const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (currentLayerChain) { @@ -1080,21 +834,21 @@ void DelegatedFrameNode::handleClippedQuad( currentLayerChain = clipNode; } handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } void DelegatedFrameNode::handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { switch (quad->material) { case viz::DrawQuad::RENDER_PASS: { const viz::RenderPassDrawQuad *renderPassQuad = viz::RenderPassDrawQuad::MaterialCast(quad); if (!renderPassQuad->mask_texture_size.IsEmpty()) { - ResourceHolder *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceTracker); Q_UNUSED(resource); // FIXME: QTBUG-67652 } QSGLayer *layer = @@ -1107,7 +861,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TEXTURE_CONTENT: { const viz::TextureDrawQuad *tquad = viz::TextureDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); QSGTexture *texture = initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate); QSizeF textureSize; @@ -1158,7 +912,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TILED_CONTENT: { const viz::TileDrawQuad *tquad = viz::TileDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); nodeHandler->setupTextureContentNode( initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate), toQt(quad->rect), toQt(tquad->tex_coord_rect), @@ -1168,17 +922,17 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::YUV_VIDEO_CONTENT: { const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad); - ResourceHolder *yResource = - findAndHoldResource(vquad->y_plane_resource_id(), resourceCandidates); - ResourceHolder *uResource = - findAndHoldResource(vquad->u_plane_resource_id(), resourceCandidates); - ResourceHolder *vResource = - findAndHoldResource(vquad->v_plane_resource_id(), resourceCandidates); - ResourceHolder *aResource = 0; + const CompositorResource *yResource = + findAndHoldResource(vquad->y_plane_resource_id(), resourceTracker); + const CompositorResource *uResource = + findAndHoldResource(vquad->u_plane_resource_id(), resourceTracker); + const CompositorResource *vResource = + findAndHoldResource(vquad->v_plane_resource_id(), resourceTracker); + const CompositorResource *aResource = nullptr; // This currently requires --enable-vp8-alpha-playback and // needs a video with alpha data to be triggered. if (vquad->a_plane_resource_id()) - aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceCandidates); + aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceTracker); nodeHandler->setupYUVVideoNode( initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()), @@ -1194,14 +948,13 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::STREAM_VIDEO_CONTENT: { const viz::StreamVideoDrawQuad *squad = viz::StreamVideoDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(squad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(squad->resource_id(), resourceTracker); MailboxTexture *texture = static_cast<MailboxTexture *>( - initAndHoldTexture(resource, quad->ShouldDrawWithBlending())); - // since this is not default TEXTURE_2D type - texture->setTarget(GL_TEXTURE_EXTERNAL_OES); + initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate, GL_TEXTURE_EXTERNAL_OES)); - nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), toQt(squad->matrix.matrix()), - currentLayerChain); + QMatrix4x4 qMatrix; + convertToQt(squad->matrix.matrix(), qMatrix); + nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), qMatrix, currentLayerChain); break; #endif // GL_OES_EGL_image_external #endif // QT_NO_OPENGL @@ -1213,75 +966,100 @@ void DelegatedFrameNode::handleQuad( } } -ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +const CompositorResource *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker) { - // ResourceHolders must survive when the scene graph destroys our node branch - QSharedPointer<ResourceHolder> &resource = m_chromiumCompositorData->resourceHolders[resourceId]; - if (!resource) - resource = candidates.take(resourceId); - Q_ASSERT(resource); - return resource.data(); + return resourceTracker->findResource(resourceId); } -void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker) { for (auto resource : quad->resources) - findAndHoldResource(resource, candidates); + findAndHoldResource(resource, resourceTracker); } -void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker) { for (const auto &quad : pass->quad_list) - holdResources(quad, candidates); + holdResources(quad, resourceTracker); } -QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) +template<class Container, class Key> +inline auto &findTexture(Container &map, Container &previousMap, const Key &key) { - // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, - // so we can't store them with the ResourceHolder in m_chromiumCompositorData. - // Hold them through a QSharedPointer solely on the root DelegatedFrameNode of the web view - // and access them through a QWeakPointer from the resource holder to find them later. - m_sgObjects.textureStrongRefs.append(resource->initTexture(quadIsAllOpaque, apiDelegate)); - return m_sgObjects.textureStrongRefs.last().data(); + auto &value = map[key]; + if (value) + return value; + value = previousMap[key]; + return value; } -void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch) +QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target) { -#ifndef QT_NO_OPENGL - QList<gl::TransferableFence> transferredFences; - { - QMutexLocker lock(&m_mutex); - QVector<MailboxTexture *> mailboxesToPull; - mailboxesToPull.reserve(mailboxesToFetch.size()); - - gpu::SyncPointManager *syncPointManager = sync_point_manager(); - scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); - Q_ASSERT(m_numPendingSyncPoints == 0); - m_numPendingSyncPoints = mailboxesToFetch.count(); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { - gpu::SyncToken &syncToken = mailboxTexture->mailboxHolder().sync_token; - const auto task = base::Bind(&DelegatedFrameNode::pullTexture, this, mailboxTexture); - if (!syncPointManager->WaitOutOfOrder(syncToken, std::move(task))) - mailboxesToPull.append(mailboxTexture); - } - if (!mailboxesToPull.isEmpty()) { - auto task = base::BindOnce(&DelegatedFrameNode::pullTextures, this, std::move(mailboxesToPull)); - gpuTaskRunner->PostTask(FROM_HERE, std::move(task)); - } - - m_mailboxesFetchedWaitCond.wait(&m_mutex); - m_textureFences.swap(transferredFences); + QSGTexture::Filtering filtering; + + if (resource->filter == GL_NEAREST) + filtering = QSGTexture::Nearest; + else if (resource->filter == GL_LINEAR) + filtering = QSGTexture::Linear; + else { + // Depends on qtdeclarative fix, see QTBUG-71322 +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) + filtering = QSGTexture::Linear; +#else + filtering = QSGTexture::Nearest; +#endif } - for (gl::TransferableFence sync : qAsConst(transferredFences)) { - // We need to wait on the fences on the Qt current context, and - // can therefore not use GLFence routines that uses a different - // concept of current context. - waitChromiumSync(&sync); - deleteChromiumSync(&sync); + if (resource->is_software) { + QSharedPointer<QSGTexture> &texture = + findTexture(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures, resource->id); + if (texture) + return texture.data(); + texture = createBitmapTexture(resource, hasAlphaChannel, apiDelegate); + texture->setFiltering(filtering); + return texture.data(); + } else { + QSharedPointer<MailboxTexture> &texture = + findTexture(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures, resource->id); + if (texture) + return texture.data(); + texture = createMailboxTexture(resource, hasAlphaChannel, target); + texture->setFiltering(filtering); + return texture.data(); } +} -#if defined(USE_OZONE) && !defined(QT_NO_OPENGL) +QSharedPointer<QSGTexture> DelegatedFrameNode::createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + Q_ASSERT(apiDelegate); + viz::SharedBitmap *sharedBitmap = resource->bitmap.get(); + gfx::Size size = resource->size; + + // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending + // to draw it but Chromium keeps this information in the quads. + // The input format is currently always Format_ARGB32_Premultiplied, so assume that all + // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion + // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to + // return false. + QImage::Format format = hasAlphaChannel ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + QImage image = sharedBitmap + ? QImage(sharedBitmap->pixels(), size.width(), size.height(), format) + : QImage(size.width(), size.height(), format); + return QSharedPointer<QSGTexture>(apiDelegate->createTextureFromImage(image.copy())); +} + +QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) +{ +#ifndef QT_NO_OPENGL + return QSharedPointer<MailboxTexture>::create(resource, hasAlphaChannel, target); +#else + Q_UNREACHABLE(); +#endif +} + +void DelegatedFrameNode::copyMailboxTextures() +{ +#if !defined(QT_NO_OPENGL) && defined(USE_OZONE) // Workaround when context is not shared QTBUG-48969 // Make slow copy between two contexts. if (!m_contextShared) { @@ -1296,13 +1074,17 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe GLuint fbo = 0; funcs->glGenFramebuffers(1, &fbo); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { + for (const QSharedPointer<MailboxTexture> &mailboxTexture : qAsConst(m_sgObjects.mailboxTextures)) { + if (mailboxTexture->m_ownsTexture) + continue; + // Read texture into QImage from shared context. // Switch to shared context. sharedContext->makeCurrent(m_offsurface.data()); funcs = sharedContext->functions(); QImage img(mailboxTexture->textureSize(), QImage::Format_RGBA8888_Premultiplied); funcs->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + mailboxTexture->m_fence->wait(); funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mailboxTexture->m_textureId, 0); GLenum status = funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -1336,69 +1118,6 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe currentContext->makeCurrent(surface); } #endif -#else - Q_UNUSED(mailboxesToFetch) -#endif //QT_NO_OPENGL -} - - -void DelegatedFrameNode::pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> textures) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - for (MailboxTexture *texture : textures) { - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - } - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(textures) -#endif -} - -void DelegatedFrameNode::pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *texture) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(texture) -#endif -} - -void DelegatedFrameNode::fenceAndUnlockQt(DelegatedFrameNode *frameNode) -{ -#ifndef QT_NO_OPENGL - if (!!gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { - // Create a fence on the Chromium GPU-thread and context - std::unique_ptr<gl::GLFence> fence = gl::GLFence::Create(); - // But transfer it to something generic since we need to read it using Qt's OpenGL. - frameNode->m_textureFences.append(fence->Transfer()); - } - if (frameNode->m_numPendingSyncPoints == 0) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&DelegatedFrameNode::unlockQt, frameNode)); -#else - Q_UNUSED(frameNode) -#endif -} - -void DelegatedFrameNode::unlockQt(DelegatedFrameNode *frameNode) -{ - QMutexLocker lock(&frameNode->m_mutex); - // Signal preprocess() the textures are ready - frameNode->m_mailboxesFetchedWaitCond.wakeOne(); } } // namespace QtWebEngineCore diff --git a/src/core/delegated_frame_node.h b/src/core/compositor/delegated_frame_node.h index e37ad08a3..34e4ba029 100644 --- a/src/core/delegated_frame_node.h +++ b/src/core/compositor/delegated_frame_node.h @@ -43,15 +43,10 @@ #include "base/containers/circular_deque.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "gpu/command_buffer/service/sync_point_manager.h" -#include "ui/gl/gl_fence.h" -#include <QMutex> -#include <QSGNode> -#include <QSharedData> -#include <QSharedPointer> -#include <QWaitCondition> + +#include <QtCore/QSharedPointer> #include <QtGui/QOffscreenSurface> +#include <QtQuick/QSGTransformNode> #include "chromium_gpu_helper.h" #include "render_widget_host_view_qt_delegate.h" @@ -72,77 +67,60 @@ class DrawPolygon; namespace QtWebEngineCore { +class CompositorResource; +class CompositorResourceTracker; class DelegatedNodeTreeHandler; class MailboxTexture; -class ResourceHolder; - -// Separating this data allows another DelegatedFrameNode to reconstruct the QSGNode tree from the mailbox textures -// and render pass information. -class ChromiumCompositorData : public QSharedData { -public: - ChromiumCompositorData() : frameDevicePixelRatio(1) { } - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceHolders; - viz::CompositorFrame frameData; - viz::CompositorFrame previousFrameData; - qreal frameDevicePixelRatio; -}; class DelegatedFrameNode : public QSGTransformNode { public: DelegatedFrameNode(); ~DelegatedFrameNode(); - void preprocess(); - void commit(ChromiumCompositorData *chromiumCompositorData, std::vector<viz::ReturnedResource> *resourcesToRelease, RenderWidgetHostViewQtDelegate *apiDelegate); + void preprocess() override; + void commit(const viz::CompositorFrame &pendingFrame, const viz::CompositorFrame &committedFrame, const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); private: void flushPolygons(base::circular_deque<std::unique_ptr<viz::DrawPolygon> > *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleClippedQuad( const viz::DrawQuad *quad, const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); - void fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch); - // Making those callbacks static bypasses base::Bind's ref-counting requirement - // of the this pointer when the callback is a method. - static void pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *mailbox); - static void pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> mailboxes); - static void fenceAndUnlockQt(DelegatedFrameNode *frameNode); - static void unlockQt(DelegatedFrameNode *frameNode); - - ResourceHolder *findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - QSGTexture *initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; + + const CompositorResource *findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker); + QSGTexture *initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate = 0, int target = -1); + QSharedPointer<QSGTexture> createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate); + QSharedPointer<MailboxTexture> createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target); + + void copyMailboxTextures(); + struct SGObjects { QVector<QPair<int, QSharedPointer<QSGLayer> > > renderPassLayers; QVector<QSharedPointer<QSGRootNode> > renderPassRootNodes; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; - } m_sgObjects; + QHash<unsigned, QSharedPointer<QSGTexture> > bitmapTextures; + QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextures; + } m_sgObjects, m_previousSGObjects; QVector<QSGNode*> m_sceneGraphNodes; - int m_numPendingSyncPoints; - QWaitCondition m_mailboxesFetchedWaitCond; - QMutex m_mutex; - QList<gl::TransferableFence> m_textureFences; #if defined(USE_OZONE) bool m_contextShared; QScopedPointer<QOffscreenSurface> m_offsurface; diff --git a/src/core/stream_video_node.cpp b/src/core/compositor/stream_video_node.cpp index 29922f866..29922f866 100644 --- a/src/core/stream_video_node.cpp +++ b/src/core/compositor/stream_video_node.cpp diff --git a/src/core/stream_video_node.h b/src/core/compositor/stream_video_node.h index 9d937791f..9d937791f 100644 --- a/src/core/stream_video_node.h +++ b/src/core/compositor/stream_video_node.h diff --git a/src/core/yuv_video_node.cpp b/src/core/compositor/yuv_video_node.cpp index 4a436d952..4a436d952 100644 --- a/src/core/yuv_video_node.cpp +++ b/src/core/compositor/yuv_video_node.cpp diff --git a/src/core/yuv_video_node.h b/src/core/compositor/yuv_video_node.h index dca8fa5e2..dca8fa5e2 100644 --- a/src/core/yuv_video_node.h +++ b/src/core/compositor/yuv_video_node.h diff --git a/src/core/config/common.pri b/src/core/config/common.pri index ddb4ca4bf..85940977a 100644 --- a/src/core/config/common.pri +++ b/src/core/config/common.pri @@ -4,6 +4,7 @@ QT_FOR_CONFIG += webenginecore gn_args += \ use_qt=true \ + closure_compile=false \ is_component_build=false \ is_shared=true \ enable_message_center=false \ @@ -11,15 +12,20 @@ gn_args += \ enable_nacl=false \ enable_remoting=false \ enable_reporting=false \ + enable_swiftshader=false \ + enable_web_auth=false \ enable_web_speech=false \ enable_widevine=true \ has_native_accessibility=false \ use_allocator_shim=false \ use_allocator=\"none\" \ + use_custom_libcxx=false \ v8_use_external_startup_data=false \ + toolkit_views=false \ treat_warnings_as_errors=false \ - enable_swiftshader=false \ - use_custom_libcxx=false + safe_browsing_mode=0 \ + optimize_webui=false \ + closure_compile=false !win32: gn_args += \ use_jumbo_build=true \ @@ -54,6 +60,12 @@ qtConfig(webengine-webrtc) { qtConfig(webengine-proprietary-codecs): gn_args += proprietary_codecs=true ffmpeg_branding=\"Chrome\" +qtConfig(webengine-extensions) { + gn_args += enable_extensions=true +} else { + gn_args += enable_extensions=false +} + precompile_header { gn_args += enable_precompiled_headers=true } else { @@ -84,7 +96,6 @@ optimize_size: gn_args += optimize_for_size=true # We don't want to apply sanitizer options to the build tools (GN, dict convert, etc). !host_build { - sanitizer: gn_args += sanitizer_keep_symbols=true sanitize_address: gn_args += is_asan=true sanitize_thread: gn_args += is_tsan=true sanitize_memory: gn_args += is_msan=true diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri index 85b948db2..04c9eca89 100644 --- a/src/core/config/linux.pri +++ b/src/core/config/linux.pri @@ -19,11 +19,14 @@ gn_args += \ qtConfig(webengine-embedded-build) { gn_args += is_desktop_linux=false - gn_args += use_gold=false -} else { - !use_gold_linker: gn_args += use_gold=false } +use_gold_linker: gn_args += use_gold=true +else: gn_args += use_gold=false + +use_lld_linker: gn_args += use_lld=true +else: gn_args += use_lld=false + clang { clang_full_path = $$which($${QMAKE_CXX}) # Remove the "/bin/clang++" part. diff --git a/src/core/config/mac_osx.pri b/src/core/config/mac_osx.pri index 4426901cf..e49df90e7 100644 --- a/src/core/config/mac_osx.pri +++ b/src/core/config/mac_osx.pri @@ -28,8 +28,6 @@ gn_args += \ clang_use_chrome_plugins=false \ mac_deployment_target=\"$${QMAKE_MACOSX_DEPLOYMENT_TARGET}\" \ mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION}\" \ - mac_views_browser=false \ - toolkit_views=false \ use_external_popup_menu=false qtConfig(webengine-spellchecker) { diff --git a/src/core/configure.json b/src/core/configure.json index 044d85527..f111247e4 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -20,11 +20,14 @@ "webengine-pulseaudio": "boolean", "webengine-spellchecker": "boolean", "webengine-native-spellchecker": "boolean", + "webengine-extensions": "boolean", "webengine-webrtc": "boolean", "webengine-geolocation": "boolean", "webengine-v8-snapshot": "boolean", "webengine-webchannel": "boolean", "webengine-kerberos": "boolean", + "webengine-widgets": "boolean", + "webengine-qml": "boolean", "alsa": { "type": "boolean", "name": "webengine-alsa" }, "pulseaudio": { "type": "boolean", "name": "webengine-pulseaudio" }, "ffmpeg": { "type": "enum", "name": "webengine-system-ffmpeg", "values": { "system": "yes", "qt": "no" } }, @@ -34,6 +37,7 @@ "printing-and-pdf": { "type": "boolean", "name": "webengine-printing-and-pdf" }, "proprietary-codecs": { "type": "boolean", "name": "webengine-proprietary-codecs" }, "spellchecker": { "type": "boolean", "name": "webengine-spellchecker" }, + "extensions": { "type": "boolean", "name": "webengine-extensions" }, "webrtc": { "type": "boolean", "name": "webengine-webrtc" } } }, @@ -82,9 +86,9 @@ ] }, "webengine-harfbuzz": { - "label": "harfbuzz >= 1.4.2", + "label": "harfbuzz >= 2.0.0", "sources": [ - { "type": "pkgConfig", "args": "harfbuzz >= 1.4.2" } + { "type": "pkgConfig", "args": "harfbuzz >= 2.0.0" } ] }, "webengine-glib": { @@ -326,6 +330,10 @@ "webengine-arm-thumb" : { "label": "thumb instruction set", "type": "hasThumbFlag" + }, + "webengine-extensions-gcc-version" : { + "label": "GCC 6 or newer", + "type": "hasGcc6OrNewer" } }, @@ -546,14 +554,13 @@ "condition": "config.macos && features.webengine-spellchecker", "output": [ "publicFeature" ] }, - "webengine-ui-delegates": { - "label": "UI Delegates", - "output": [ "privateFeature" ] - }, - "webengine-testsupport": { - "label": "Test Support", - "autoDetect": "features.private_tests || call.isTestsInBuildParts", - "output": [ "privateFeature" ] + "webengine-extensions": { + "label": "Extensions", + "purpose": "Enables Chromium extensions within certain limits. Currently used for enabling the pdf viewer.", + "section": "WebEngine", + "condition": "features.webengine-printing-and-pdf && (tests.webengine-extensions-gcc-version || config.clang || !config.gcc)", + "autoDetect": "features.webengine-printing-and-pdf", + "output": [ "publicFeature" ] }, "webengine-webrtc": { "label": "WebRTC", @@ -657,6 +664,19 @@ "label": "Thumb instruction set", "condition": "config.linux && features.webengine-embedded-build && arch.arm && tests.webengine-arm-thumb", "output": [ "privateFeature" ] + }, + "webengine-widgets": { + "label": "Qt WebEngine Widgets", + "purpose": "Provides WebEngine Widgets support.", + "section": "WebEngine", + "condition": "module.widgets", + "output": [ "privateFeature" ] + }, + "webengine-qml": { + "label": "Qt WebEngine Qml", + "purpose": "Provides WebEngine Qml support.", + "section": "WebEngine", + "output": [ "privateFeature" ] } }, @@ -705,8 +725,10 @@ "summary": [ { - "section": "Qt WebEngine", + "section": "Qt WebEngineCore", "entries": [ + "webengine-widgets", + "webengine-qml", "webengine-embedded-build", "webengine-pepper-plugins", "webengine-printing-and-pdf", @@ -719,6 +741,7 @@ "webengine-webchannel", "webengine-v8-snapshot", "webengine-kerberos", + "webengine-extensions", { "type": "feature", "args": "webengine-v8-snapshot-support", diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 0a51cc261..7440771cb 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -42,12 +42,17 @@ #include "base/json/json_reader.h" #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/message_loop/message_loop.h" +#include "base/task/post_task.h" +#include "base/threading/thread_restrictions.h" #if QT_CONFIG(webengine_spellchecker) #include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" #endif +#include "components/guest_view/browser/guest_view_base.h" #include "components/network_hints/browser/network_hints_message_filter.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/common/url_schemes.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" @@ -66,10 +71,12 @@ #include "content/public/common/service_names.mojom.h" #include "content/public/common/url_constants.h" #include "media/media_buildflags.h" +#include "extensions/buildflags/buildflags.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "printing/buildflags/buildflags.h" #include "net/ssl/client_cert_identity.h" +#include "net/ssl/client_cert_store.h" #include "services/proxy_resolver/proxy_resolver_service.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" @@ -95,12 +102,13 @@ #include "login_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/network_delegate_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" +#include "platform_notification_service_qt.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/printing_message_filter_qt.h" #endif #include "profile_qt.h" +#include "profile_io_data_qt.h" #include "quota_permission_context_qt.h" #include "renderer_host/user_resource_controller_host.h" #include "service/service_qt.h" @@ -114,18 +122,6 @@ #include "ui/base/resource/resource_bundle.h" #endif -#if defined(USE_NSS_CERTS) -#include "net/ssl/client_cert_store_nss.h" -#endif - -#if defined(OS_WIN) -#include "net/ssl/client_cert_store_win.h" -#endif - -#if defined(OS_MACOSX) -#include "net/ssl/client_cert_store_mac.h" -#endif - #if QT_CONFIG(webengine_pepper_plugins) #include "content/public/browser/browser_ppapi_host.h" #include "ppapi/host/ppapi_host.h" @@ -136,6 +132,16 @@ #include "location_provider_qt.h" #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/extensions_browser_client_qt.h" +#include "extensions/browser/extension_message_filter.h" +#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h" +#include "extensions/browser/io_thread_extension_message_filter.h" +#include "extensions/common/constants.h" +#include "common/extensions/extensions_client_qt.h" +#include "renderer_host/resource_dispatcher_host_delegate_qt.h" +#endif + #include <QGuiApplication> #include <QLocale> #ifndef QT_NO_OPENGL @@ -157,7 +163,7 @@ public: { QString platform = qApp->platformName().toLower(); QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); - if (platform == QLatin1String("xcb")) { + if (platform == QLatin1String("xcb") || platform == QLatin1String("offscreen")) { if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2) m_handle = pni->nativeResourceForContext(QByteArrayLiteral("eglcontext"), qtContext); else @@ -248,10 +254,10 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* { const int id = host->GetID(); Profile *profile = Profile::FromBrowserContext(host->GetBrowserContext()); - content::BrowserThread::PostTaskAndReplyWithResult( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&net::URLRequestContextGetter::GetURLRequestContext, base::Unretained(profile->GetRequestContext())), - base::Bind(&ContentBrowserClientQt::AddNetworkHintsMessageFilter, base::Unretained(this), id)); + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&net::URLRequestContextGetter::GetURLRequestContext, base::Unretained(profile->GetRequestContext())), + base::BindOnce(&ContentBrowserClientQt::AddNetworkHintsMessageFilter, base::Unretained(this), id)); // FIXME: Add a settings variable to enable/disable the file scheme. content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme(id, url::kFileScheme); @@ -260,6 +266,11 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* #if QT_CONFIG(webengine_printing_and_pdf) host->AddFilter(new PrintingMessageFilterQt(host->GetID())); #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) + host->AddFilter(new extensions::ExtensionMessageFilter(host->GetID(), host->GetBrowserContext())); + host->AddFilter(new extensions::IOThreadExtensionMessageFilter(host->GetID(), host->GetBrowserContext())); + host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(host->GetID(), host->GetBrowserContext())); +#endif //ENABLE_EXTENSIONS service_manager::mojom::ServicePtr service; *service_request = mojo::MakeRequest(&service); @@ -274,7 +285,11 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* void ContentBrowserClientQt::ResourceDispatcherHostCreated() { +#if BUILDFLAG(ENABLE_EXTENSIONS) + m_resourceDispatcherHostDelegate.reset(new ResourceDispatcherHostDelegateQt); +#else m_resourceDispatcherHostDelegate.reset(new content::ResourceDispatcherHostDelegate); +#endif content::ResourceDispatcherHost::Get()->SetDelegate(m_resourceDispatcherHostDelegate.get()); } @@ -293,6 +308,10 @@ content::MediaObserver *ContentBrowserClientQt::GetMediaObserver() void ContentBrowserClientQt::OverrideWebkitPrefs(content::RenderViewHost *rvh, content::WebPreferences *web_prefs) { if (content::WebContents *webContents = rvh->GetDelegate()->GetAsWebContents()) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (guest_view::GuestViewBase::IsGuest(webContents)) + return; +#endif // BUILDFLAG(ENABLE_EXTENSIONS) WebContentsDelegateQt* delegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); if (delegate) delegate->overrideWebPreferences(webContents, web_prefs); @@ -382,17 +401,8 @@ std::unique_ptr<net::ClientCertStore> ContentBrowserClientQt::CreateClientCertSt { if (!resource_context) return nullptr; -#if defined(USE_NSS_CERTS) - // FIXME: Give it a proper callback for a password delegate. - return std::unique_ptr<net::ClientCertStore>( - new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); -#elif defined(OS_WIN) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); -#elif defined(OS_MACOSX) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); -#else - return nullptr; -#endif + + return ProfileIODataQt::FromResourceContext(resource_context)->CreateClientCertStore(); } std::string ContentBrowserClientQt::GetApplicationLocale() @@ -421,6 +431,11 @@ void ContentBrowserClientQt::GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes->push_back(content::kChromeDevToolsScheme); } +void ContentBrowserClientQt::GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes) +{ + additional_schemes->push_back(content::kChromeDevToolsScheme); +} + #if defined(Q_OS_LINUX) void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) { @@ -441,7 +456,7 @@ void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base: void ContentBrowserClientQt::DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) { browser_host->GetPpapiHost()->AddHostFactoryFilter( - base::WrapUnique(new QtWebEngineCore::PepperHostFactoryQt(browser_host))); + std::make_unique<QtWebEngineCore::PepperHostFactoryQt>(browser_host)); } #endif @@ -450,6 +465,13 @@ content::DevToolsManagerDelegate* ContentBrowserClientQt::GetDevToolsManagerDele return new DevToolsManagerDelegateQt; } +content::PlatformNotificationService *ContentBrowserClientQt::GetPlatformNotificationService() +{ + if (!m_platformNotificationService) + m_platformNotificationService = std::make_unique<PlatformNotificationServiceQt>(); + return m_platformNotificationService.get(); +} + // This is a really complicated way of doing absolutely nothing, but Mojo demands it: class ServiceDriver : public blink::mojom::InsecureInputService @@ -618,11 +640,18 @@ void ContentBrowserClientQt::AddNetworkHintsMessageFilter(int render_process_id, if (!host) return; - content::BrowserMessageFilter *network_hints_message_filter( - new network_hints::NetworkHintsMessageFilter(context->host_resolver())); + content::BrowserMessageFilter *network_hints_message_filter = + new network_hints::NetworkHintsMessageFilter(render_process_id); host->AddFilter(network_hints_message_filter); } +bool ContentBrowserClientQt::ShouldEnableStrictSiteIsolation() +{ + // mirroring AwContentBrowserClient, CastContentBrowserClient and + // HeadlessContentBrowserClient + return false; +} + bool ContentBrowserClientQt::AllowGetCookie(const GURL &url, const GURL &first_party, const net::CookieList & /*cookie_list*/, @@ -671,7 +700,7 @@ bool ContentBrowserClientQt::AllowServiceWorker(const GURL &scope, // We control worker access to FS and indexed-db using cookie permissions, this is mirroring Chromium's logic. void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &/*render_frames*/, + const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/, base::Callback<void(bool)> callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); @@ -682,7 +711,7 @@ void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url, bool ContentBrowserClientQt::AllowWorkerIndexedDB(const GURL &url, const base::string16 &/*name*/, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &/*render_frames*/) + const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); NetworkDelegateQt *networkDelegate = static_cast<NetworkDelegateQt *>(context->GetRequestContext()->network_delegate()); @@ -715,15 +744,13 @@ bool ContentBrowserClientQt::HandleExternalProtocol( Q_UNUSED(child_id); Q_UNUSED(navigation_data); - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&LaunchURL, - url, - web_contents_getter, - page_transition, - is_main_frame, - has_user_gesture)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&LaunchURL, + url, + web_contents_getter, + page_transition, + is_main_frame, + has_user_gesture)); return true; } @@ -737,9 +764,24 @@ scoped_refptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegat bool first_auth_attempt, LoginAuthRequiredCallback auth_required_callback) { - return base::MakeRefCounted<LoginDelegateQt>(authInfo, web_contents_getter, url, first_auth_attempt, std::move(auth_required_callback)); + auto loginDelegate = base::MakeRefCounted<LoginDelegateQt>(authInfo, web_contents_getter, url, first_auth_attempt, std::move(auth_required_callback)); + loginDelegate->triggerDialog(); + return loginDelegate; } -} // namespace QtWebEngineCore +bool ContentBrowserClientQt::ShouldIsolateErrorPage(bool in_main_frame) +{ + Q_UNUSED(in_main_frame); + return false; +} + +bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (effective_url.SchemeIs(extensions::kExtensionScheme)) + return true; +#endif + return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); +} -DEFINE_WEB_CONTENTS_USER_DATA_KEY(QtWebEngineCore::ServiceDriver); +} // namespace QtWebEngineCore diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index b2761b311..403ff3a9d 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -108,10 +108,13 @@ public: std::unique_ptr<content::ClientCertificateDelegate> delegate) override; std::unique_ptr<net::ClientCertStore> CreateClientCertStore(content::ResourceContext *resource_context) override; content::DevToolsManagerDelegate *GetDevToolsManagerDelegate() override; + content::PlatformNotificationService *GetPlatformNotificationService() override; std::string GetApplicationLocale() override; std::string GetAcceptLangs(content::BrowserContext* context) override; void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; + + void GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes) override; void GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes) override; void BindInterfaceRequestFromFrame(content::RenderFrameHost* render_frame_host, @@ -135,6 +138,7 @@ public: bool user_gesture, bool opener_suppressed, bool* no_javascript_access) override; + bool ShouldEnableStrictSiteIsolation() override; bool AllowGetCookie(const GURL& url, const GURL& first_party, @@ -161,17 +165,19 @@ public: void AllowWorkerFileSystem(const GURL &url, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &render_frames, + const std::vector<content::GlobalFrameRoutingId> &render_frames, base::Callback<void(bool)> callback) override; bool AllowWorkerIndexedDB(const GURL &url, const base::string16 &name, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &render_frames) override; + const std::vector<content::GlobalFrameRoutingId> &render_frames) override; #if QT_CONFIG(webengine_geolocation) std::unique_ptr<device::LocationProvider> OverrideSystemLocationProvider() override; #endif + bool ShouldIsolateErrorPage(bool in_main_frame) override; + bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) override; #if defined(Q_OS_LINUX) void GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) override; @@ -205,6 +211,7 @@ private: BrowserMainPartsQt* m_browserMainParts; std::unique_ptr<content::ResourceDispatcherHostDelegate> m_resourceDispatcherHostDelegate; + std::unique_ptr<content::PlatformNotificationService> m_platformNotificationService; scoped_refptr<ShareGroupQtQuick> m_shareGroupQtQuick; std::unique_ptr<service_manager::BinderRegistry> m_frameInterfaces; std::unique_ptr<service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>> m_frameInterfacesParameterized; diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index df1bc303d..9bfc8eede 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -56,7 +56,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "net/qrc_protocol_handler_qt.h" #include "type_conversion.h" #include <QCoreApplication> @@ -85,6 +84,14 @@ const char kWidevineCdmFileName[] = #endif #endif +#if QT_CONFIG(webengine_printing_and_pdf) +#include "pdf/pdf.h" +#include "pdf/pdf_ppapi.h" +const char kPdfPluginMimeType[] = "application/x-google-chrome-pdf"; +const char kPdfPluginPath[] = "internal-pdf-viewer/"; +const char kPdfPluginSrc[] = "src"; +#endif // QT_CONFIG(webengine_printing_and_pdf) + static QString webenginePluginsPath() { // Look for plugins in /plugins/webengine or application dir. @@ -99,7 +106,6 @@ static QString webenginePluginsPath() } #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) - #if defined(Q_OS_WIN) #include <shlobj.h> static QString getLocalAppDataDir() @@ -235,10 +241,30 @@ void AddPepperFlashFromCommandLine(std::vector<content::PepperPluginInfo>* plugi plugins->push_back(CreatePepperFlashInfo(base::FilePath(flash_path), flash_version)); } +void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) +{ +#if QT_CONFIG(webengine_printing_and_pdf) + content::PepperPluginInfo pdf_info; + pdf_info.is_internal = true; + pdf_info.is_out_of_process = true; + pdf_info.name = "Chromium PDF Viewer"; + pdf_info.description = "Portable Document Format"; + pdf_info.path = base::FilePath::FromUTF8Unsafe(kPdfPluginPath); + content::WebPluginMimeType pdf_mime_type(kPdfPluginMimeType, "pdf", "Portable Document Format"); + pdf_info.mime_types.push_back(pdf_mime_type); + pdf_info.internal_entry_points.get_interface = chrome_pdf::PPP_GetInterface; + pdf_info.internal_entry_points.initialize_module = chrome_pdf::PPP_InitializeModule; + pdf_info.internal_entry_points.shutdown_module = chrome_pdf::PPP_ShutdownModule; + pdf_info.permissions = ppapi::PERMISSION_PRIVATE | ppapi::PERMISSION_DEV | ppapi::PERMISSION_PDF; + plugins->push_back(pdf_info); +#endif // QT_CONFIG(webengine_printing_and_pdf) +} + namespace QtWebEngineCore { void ContentClientQt::AddPepperPlugins(std::vector<content::PepperPluginInfo>* plugins) { + ComputeBuiltInPlugins(plugins); AddPepperFlashFromSystem(plugins); AddPepperFlashFromCommandLine(plugins); } @@ -330,7 +356,6 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path, } #endif // defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) - void ContentClientQt::AddContentDecryptionModules(std::vector<content::CdmInfo> *cdms, std::vector<media::CdmHostFilePath> *cdm_host_file_paths) { @@ -386,6 +411,11 @@ void ContentClientQt::AddContentDecryptionModules(std::vector<content::CdmInfo> } } +void ContentClientQt::AddAdditionalSchemes(Schemes* schemes) +{ + schemes->standard_schemes.push_back("chrome-extension"); +} + std::string ContentClientQt::getUserAgent() { // Mention the Chromium version we're based on to get passed stupid UA-string-based feature detection (several WebRTC demos need this) @@ -401,6 +431,11 @@ base::RefCountedMemory *ContentClientQt::GetDataResourceBytes(int resource_id) c return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(resource_id); } +gfx::Image &ContentClientQt::GetNativeImageNamed(int resource_id) const +{ + return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); +} + base::string16 ContentClientQt::GetLocalizedString(int message_id) const { return l10n_util::GetStringUTF16(message_id); diff --git a/src/core/content_client_qt.h b/src/core/content_client_qt.h index fbafa6a4b..bee79986c 100644 --- a/src/core/content_client_qt.h +++ b/src/core/content_client_qt.h @@ -56,9 +56,11 @@ public: #endif void AddContentDecryptionModules(std::vector<content::CdmInfo> *cdms, std::vector<media::CdmHostFilePath> *cdm_host_file_paths) override; + void AddAdditionalSchemes(Schemes* schemes) override; base::StringPiece GetDataResource(int, ui::ScaleFactor) const override; - base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + gfx::Image &GetNativeImageNamed(int resource_id) const override; std::string GetUserAgent() const override { return getUserAgent(); } base::string16 GetLocalizedString(int message_id) const override; std::string GetProduct() const override; diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index 2811d5545..dc32bb8c1 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -40,17 +40,18 @@ #include "content_main_delegate_qt.h" #include "base/command_line.h" -#include <base/i18n/rtl.h> +#include "base/i18n/rtl.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" -#include <chrome/grit/generated_resources.h> +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_paths.h" #include "ui/base/resource/resource_bundle.h" -#include <ui/base/webui/jstemplate_builder.h> +#include "ui/base/webui/jstemplate_builder.h" #include "net/grit/net_resources.h" #include "net/base/net_module.h" #include "services/service_manager/sandbox/switches.h" @@ -59,6 +60,7 @@ #include "content_client_qt.h" #include "renderer/content_renderer_client_qt.h" #include "type_conversion.h" +#include "web_engine_context.h" #include "web_engine_library_info.h" #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) @@ -71,6 +73,10 @@ #include <QtCore/qcoreapplication.h> +namespace content { +ContentClient *GetContentClient(); +} + namespace QtWebEngineCore { // The logic of this function is based on chrome/common/net/net_resource_provider.cc @@ -167,6 +173,12 @@ content::ContentBrowserClient *ContentMainDelegateQt::CreateContentBrowserClient return m_browserClient.get(); } +content::ContentGpuClient *ContentMainDelegateQt::CreateContentGpuClient() +{ + m_gpuClient.reset(new ContentGpuClientQt); + return m_gpuClient.get(); +} + content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient() { #if defined(OS_LINUX) @@ -221,7 +233,8 @@ bool ContentMainDelegateQt::BasicStartupComplete(int *exit_code) #if QT_CONFIG(webengine_spellchecker) SafeOverridePath(base::DIR_APP_DICTIONARIES, WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES)); #endif - SetContentClient(new ContentClientQt); + if (!content::GetContentClient()) + content::SetContentClient(new ContentClientQt); url::CustomScheme::LoadSchemes(base::CommandLine::ForCurrentProcess()); diff --git a/src/core/content_main_delegate_qt.h b/src/core/content_main_delegate_qt.h index c06afb0fb..4d2f33792 100644 --- a/src/core/content_main_delegate_qt.h +++ b/src/core/content_main_delegate_qt.h @@ -42,6 +42,7 @@ #include "content/public/app/content_main_delegate.h" +#include "compositor/content_gpu_client_qt.h" #include "content_browser_client_qt.h" #include "content_utility_client_qt.h" @@ -56,12 +57,14 @@ public: void PreSandboxStartup() override; content::ContentBrowserClient* CreateContentBrowserClient() override; + content::ContentGpuClient* CreateContentGpuClient() override; content::ContentRendererClient* CreateContentRendererClient() override; content::ContentUtilityClient* CreateContentUtilityClient() override; bool BasicStartupComplete(int* /*exit_code*/) override; private: std::unique_ptr<ContentBrowserClientQt> m_browserClient; + std::unique_ptr<ContentGpuClientQt> m_gpuClient; std::unique_ptr<ContentUtilityClientQt> m_utilityClient; }; diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index e92dd60ff..f4cb8c9da 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -38,6 +38,7 @@ qtConfig(egl): CONFIG += egl INCLUDEPATH += $$PWD $$PWD/api SOURCES = \ + accessibility_activation_observer.cpp \ accessibility_tree_formatter_qt.cpp \ authentication_dialog_controller.cpp \ browser_accessibility_manager_qt.cpp \ @@ -46,21 +47,24 @@ SOURCES = \ browser_main_parts_qt.cpp \ browser_message_filter_qt.cpp \ certificate_error_controller.cpp \ - chromium_gpu_helper.cpp \ chromium_overrides.cpp \ client_cert_select_controller.cpp \ clipboard_qt.cpp \ color_chooser_qt.cpp \ color_chooser_controller.cpp \ + command_line_pref_store_qt.cpp \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ - compositor.cpp \ + compositor/chromium_gpu_helper.cpp \ + compositor/compositor.cpp \ + compositor/compositor_resource_tracker.cpp \ + compositor/content_gpu_client_qt.cpp \ + compositor/delegated_frame_node.cpp \ content_client_qt.cpp \ content_browser_client_qt.cpp \ content_main_delegate_qt.cpp \ content_utility_client_qt.cpp \ - delegated_frame_node.cpp \ desktop_screen_qt.cpp \ devtools_frontend_qt.cpp \ devtools_manager_delegate_qt.cpp \ @@ -72,17 +76,19 @@ SOURCES = \ login_delegate_qt.cpp \ media_capture_devices_dispatcher.cpp \ native_web_keyboard_event_qt.cpp \ + net/client_cert_override.cpp \ + net/client_cert_store_data.cpp \ net/cookie_monster_delegate_qt.cpp \ net/custom_protocol_handler.cpp \ net/network_delegate_qt.cpp \ net/proxy_config_service_qt.cpp \ - net/qrc_protocol_handler_qt.cpp \ + net/qrc_url_scheme_handler.cpp \ net/ssl_host_state_delegate_qt.cpp \ net/url_request_context_getter_qt.cpp \ net/url_request_custom_job.cpp \ net/url_request_custom_job_delegate.cpp \ net/url_request_custom_job_proxy.cpp \ - net/url_request_qrc_job_qt.cpp \ + net/url_request_notification.cpp \ net/webui_controller_factory_qt.cpp \ ozone/gl_context_qt.cpp \ ozone/gl_ozone_egl_qt.cpp \ @@ -93,6 +99,7 @@ SOURCES = \ ozone/platform_window_qt.cpp \ ozone/surface_factory_qt.cpp \ permission_manager_qt.cpp \ + platform_notification_service_qt.cpp \ process_main.cpp \ profile_adapter.cpp \ profile_adapter_client.cpp \ @@ -113,19 +120,25 @@ SOURCES = \ resource_bundle_qt.cpp \ resource_context_qt.cpp \ service/service_qt.cpp \ + touch_handle_drawable_qt.cpp \ + touch_selection_controller_client_qt.cpp \ + touch_selection_menu_controller.cpp \ type_conversion.cpp \ + user_notification_controller.cpp \ user_script.cpp \ visited_links_manager_qt.cpp \ web_contents_adapter.cpp \ web_contents_delegate_qt.cpp \ web_contents_view_qt.cpp \ web_engine_context.cpp \ + web_engine_context_threads.cpp \ web_engine_error.cpp \ web_engine_library_info.cpp \ web_engine_settings.cpp \ web_event_factory.cpp HEADERS = \ + accessibility_activation_observer.h \ authentication_dialog_controller_p.h \ authentication_dialog_controller.h \ build_config_qt.h \ @@ -136,43 +149,49 @@ HEADERS = \ browser_message_filter_qt.h \ certificate_error_controller_p.h \ certificate_error_controller.h \ - chromium_overrides.h \ client_cert_select_controller.h \ clipboard_qt.h \ + command_line_pref_store_qt.h \ color_chooser_qt.h \ color_chooser_controller_p.h \ color_chooser_controller.h \ common/qt_messages.h \ common/user_script_data.h \ - compositor.h \ + compositor/chromium_gpu_helper.h \ + compositor/compositor.h \ + compositor/compositor_resource.h \ + compositor/compositor_resource_tracker.h \ + compositor/content_gpu_client_qt.h \ + compositor/delegated_frame_node.h \ content_client_qt.h \ content_browser_client_qt.h \ content_main_delegate_qt.h \ content_utility_client_qt.h \ - delegated_frame_node.h \ desktop_screen_qt.h \ devtools_frontend_qt.h \ devtools_manager_delegate_qt.h \ download_manager_delegate_qt.h \ - chromium_gpu_helper.h \ favicon_manager.h \ file_picker_controller.h \ global_descriptors_qt.h \ javascript_dialog_controller_p.h \ javascript_dialog_controller.h \ javascript_dialog_manager_qt.h \ + locked_ptr.h \ login_delegate_qt.h \ media_capture_devices_dispatcher.h \ + net/client_cert_override.h \ + net/client_cert_store_data.h \ net/cookie_monster_delegate_qt.h \ net/custom_protocol_handler.h \ net/network_delegate_qt.h \ - net/qrc_protocol_handler_qt.h \ + net/qrc_url_scheme_handler.h \ net/ssl_host_state_delegate_qt.h \ net/url_request_context_getter_qt.h \ net/url_request_custom_job.h \ net/url_request_custom_job_delegate.h \ net/url_request_custom_job_proxy.h \ - net/url_request_qrc_job_qt.h \ + net/url_request_notification.h \ net/webui_controller_factory_qt.h \ ozone/gl_context_qt.h \ ozone/gl_ozone_egl_qt.h \ @@ -183,6 +202,7 @@ HEADERS = \ ozone/platform_window_qt.h \ ozone/surface_factory_qt.h \ permission_manager_qt.h \ + platform_notification_service_qt.h \ process_main.h \ profile_adapter.h \ profile_adapter_client.h \ @@ -207,7 +227,12 @@ HEADERS = \ request_controller.h \ resource_context_qt.h \ service/service_qt.h \ + touch_handle_drawable_client.h \ + touch_handle_drawable_qt.h \ + touch_selection_controller_client_qt.h \ + touch_selection_menu_controller.h \ type_conversion.h \ + user_notification_controller.h \ user_script.h \ visited_links_manager_qt.h \ web_contents_adapter.h \ @@ -266,12 +291,14 @@ qtConfig(webengine-printing-and-pdf) { contains(QT_CONFIG, opengl) { SOURCES += \ - yuv_video_node.cpp \ - stream_video_node.cpp + compositor/compositor_resource_fence.cpp \ + compositor/stream_video_node.cpp \ + compositor/yuv_video_node.cpp HEADERS += \ - yuv_video_node.h \ - stream_video_node.h + compositor/compositor_resource_fence.h \ + compositor/stream_video_node.h \ + compositor/yuv_video_node.h } qtConfig(webengine-geolocation) { @@ -286,3 +313,39 @@ qtConfig(webengine-webchannel) { SOURCES += renderer/web_channel_ipc_transport.cpp \ renderer_host/web_channel_ipc_transport_host.cpp } + +qtConfig(webengine-extensions) { + SOURCES += \ + common/extensions/extensions_api_provider_qt.cpp \ + common/extensions/extensions_client_qt.cpp \ + extensions/component_extension_resource_manager_qt.cpp \ + extensions/extension_system_qt.cpp \ + extensions/extension_system_factory_qt.cpp \ + extensions/extension_web_contents_observer_qt.cpp \ + extensions/extensions_api_client_qt.cpp \ + extensions/extensions_browser_api_provider_qt.cpp \ + extensions/extensions_browser_client_qt.cpp \ + extensions/mime_handler_view_guest_delegate_qt.cpp \ + renderer/extensions/extensions_dispatcher_delegate_qt.cpp \ + renderer/extensions/extensions_renderer_client_qt.cpp \ + renderer/extensions/renderer_permissions_policy_delegate_qt.cpp \ + renderer/extensions/resource_request_policy_qt.cpp \ + renderer_host/resource_dispatcher_host_delegate_qt.cpp + + HEADERS += \ + common/extensions/extensions_api_provider_qt.h \ + common/extensions/extensions_client_qt.h \ + extensions/component_extension_resource_manager_qt.h \ + extensions/extension_system_qt.h \ + extensions/extension_system_factory_qt.h \ + extensions/extension_web_contents_observer_qt.h \ + extensions/extensions_api_client_qt.h \ + extensions/extensions_browser_api_provider_qt.h \ + extensions/extensions_browser_client_qt.h \ + extensions/mime_handler_view_guest_delegate_qt.h \ + renderer/extensions/extensions_dispatcher_delegate_qt.h \ + renderer/extensions/extensions_renderer_client_qt.h \ + renderer/extensions/renderer_permissions_policy_delegate_qt.h \ + renderer/extensions/resource_request_policy_qt.h \ + renderer_host/resource_dispatcher_host_delegate_qt.h +} diff --git a/src/core/core_common.pri b/src/core/core_common.pri index 5f9f3c4f6..8375d89e5 100644 --- a/src/core/core_common.pri +++ b/src/core/core_common.pri @@ -13,3 +13,7 @@ CONFIG -= ltcg # Chromium requires C++14 CONFIG += c++14 + +#QTBUG-73216 ci has to be updated with latest yocto +boot2qt: CONFIG -= use_gold_linker + diff --git a/src/core/core_gn_config.pri b/src/core/core_gn_config.pri index 9b0145dfc..b76cdc81a 100644 --- a/src/core/core_gn_config.pri +++ b/src/core/core_gn_config.pri @@ -1,11 +1,19 @@ +include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) +QT_FOR_CONFIG += webenginecore webenginecore-private + CONFIG = gn_generator $$CONFIG GN_SRC_DIR = $$PWD GN_FILE = $$OUT_PWD/$$getConfigDir()/BUILD.gn GN_FIND_MOCABLES_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_find_mocables.py) GN_RUN_BINARY_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_run_binary.py) GN_IMPORTS = $$PWD/qtwebengine.gni -GN_INCLUDES = $$PWD/qtwebengine_sources.gni $$PWD/qtwebengine_resources.gni +qtConfig (webengine-extensions) { + GN_INCLUDES += $$PWD/qtwebengine_sources.gni $$PWD/qtwebengine_resources.gni $$PWD/common/extensions/api/qtwebengine_extensions_features.gni +} else { + GN_INCLUDES = $$PWD/qtwebengine_sources.gni $$PWD/qtwebengine_resources.gni +} GN_CORE_INCLUDE_DIRS = $$PWD/service GN_CREATE_PRI = true QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE + diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp index bd9e0ebe7..40e30e008 100644 --- a/src/core/devtools_frontend_qt.cpp +++ b/src/core/devtools_frontend_qt.cpp @@ -56,11 +56,13 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" #include "base/values.h" #include "chrome/common/url_constants.h" #include "components/prefs/in_memory_pref_store.h" #include "components/prefs/json_pref_store.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_handle.h" @@ -109,9 +111,9 @@ public: ~ResponseWriter() override; // URLFetcherResponseWriter overrides: - int Initialize(const net::CompletionCallback &callback) override; - int Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback) override; - int Finish(int net_error, const net::CompletionCallback &callback) override; + int Initialize(net::CompletionOnceCallback callback) override; + int Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) override; + int Finish(int net_error, net::CompletionOnceCallback callback) override; private: base::WeakPtr<DevToolsFrontendQt> shell_devtools_; @@ -126,12 +128,12 @@ ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools, ResponseWriter::~ResponseWriter() {} -int ResponseWriter::Initialize(const net::CompletionCallback& callback) +int ResponseWriter::Initialize(net::CompletionOnceCallback callback) { return net::OK; } -int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback) +int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) { std::string chunk = std::string(buffer->data(), num_bytes); if (!base::IsStringUTF8(chunk)) @@ -140,15 +142,15 @@ int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, const net::Compl base::Value *id = new base::Value(stream_id_); base::Value *chunkValue = new base::Value(chunk); - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&DevToolsFrontendQt::CallClientFunction, shell_devtools_, "DevToolsAPI.streamWrite", base::Owned(id), base::Owned(chunkValue), nullptr)); return num_bytes; } -int ResponseWriter::Finish(int net_error, const net::CompletionCallback &callback) +int ResponseWriter::Finish(int net_error, net::CompletionOnceCallback callback) { return net::OK; } diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp index f0f0c93b9..c8b75a893 100644 --- a/src/core/download_manager_delegate_qt.cpp +++ b/src/core/download_manager_delegate_qt.cpp @@ -166,7 +166,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* suggestedFilename += QStringLiteral(".") + mimeType.preferredSuffix(); } - QDir defaultDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QDir defaultDownloadDirectory(m_profileAdapter->downloadPath()); QFileInfo suggestedFile(defaultDownloadDirectory.absoluteFilePath(suggestedFilename)); QString suggestedFilePath = suggestedFile.absoluteFilePath(); diff --git a/src/core/extensions/component_extension_resource_manager_qt.cpp b/src/core/extensions/component_extension_resource_manager_qt.cpp new file mode 100644 index 000000000..d8326400e --- /dev/null +++ b/src/core/extensions/component_extension_resource_manager_qt.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// based on chrome/browser/extensions/chrome_component_extension_resource_manager.cc: +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "component_extension_resource_manager_qt.h" + +#include "base/logging.h" +#include "base/path_service.h" +#include "chrome/grit/component_extension_resources_map.h" + +namespace extensions { + +ComponentExtensionResourceManagerQt::ComponentExtensionResourceManagerQt() +{ + AddComponentResourceEntries(kComponentExtensionResources, + kComponentExtensionResourcesSize); +} + +ComponentExtensionResourceManagerQt::~ComponentExtensionResourceManagerQt() {} + +bool ComponentExtensionResourceManagerQt::IsComponentExtensionResource(const base::FilePath &extension_path, + const base::FilePath &resource_path, + int *resource_id) const +{ + base::FilePath directory_path = extension_path; + base::FilePath resources_dir; + base::FilePath relative_path; + if (!base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &resources_dir) + || !resources_dir.AppendRelativePath(directory_path, &relative_path)) { + return false; + } + + relative_path = relative_path.Append(resource_path); + relative_path = relative_path.NormalizePathSeparators(); + + std::map<base::FilePath, int>::const_iterator entry = path_to_resource_id_.find(relative_path); + if (entry != path_to_resource_id_.end()) + *resource_id = entry->second; + + return entry != path_to_resource_id_.end(); +} + +void ComponentExtensionResourceManagerQt::AddComponentResourceEntries(const GritResourceMap *entries, size_t size) +{ + for (size_t i = 0; i < size; ++i) { + base::FilePath resource_path = base::FilePath().AppendASCII(entries[i].name); + resource_path = resource_path.NormalizePathSeparators(); + + DCHECK(path_to_resource_id_.find(resource_path) == path_to_resource_id_.end()); + path_to_resource_id_[resource_path] = entries[i].value; + } +} + +} // namespace extensions diff --git a/src/core/extensions/component_extension_resource_manager_qt.h b/src/core/extensions/component_extension_resource_manager_qt.h new file mode 100644 index 000000000..f12edf61e --- /dev/null +++ b/src/core/extensions/component_extension_resource_manager_qt.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENT_EXTENSION_RESOURCE_MANAGER_QT_H_ +#define COMPONENT_EXTENSION_RESOURCE_MANAGER_QT_H_ + +#include <map> + +#include "base/files/file_path.h" +#include "extensions/browser/component_extension_resource_manager.h" + +struct GritResourceMap; + +namespace extensions { + +class ComponentExtensionResourceManagerQt : public ComponentExtensionResourceManager +{ +public: + ComponentExtensionResourceManagerQt(); + ~ComponentExtensionResourceManagerQt() override; + + // Overridden from ComponentExtensionResourceManager: + bool IsComponentExtensionResource(const base::FilePath &extension_path, + const base::FilePath &resource_path, + int *resource_id) const override; + +private: + void AddComponentResourceEntries(const GritResourceMap* entries, size_t size); + + // A map from a resource path to the resource ID. Used by + // IsComponentExtensionResource. + std::map<base::FilePath, int> path_to_resource_id_; + + DISALLOW_COPY_AND_ASSIGN(ComponentExtensionResourceManagerQt); +}; + +} // namespace extensions + +#endif // COMPONENT_EXTENSION_RESOURCE_MANAGER_QT_H_ diff --git a/src/core/extensions/extension_system_factory_qt.cpp b/src/core/extensions/extension_system_factory_qt.cpp new file mode 100644 index 000000000..41ba31214 --- /dev/null +++ b/src/core/extensions/extension_system_factory_qt.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extension_system_factory_qt.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/declarative_user_script_manager_factory.h" +#include "extensions/browser/event_router_factory.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/browser/process_manager_factory.h" +#include "extensions/browser/renderer_startup_helper.h" + +namespace extensions { + +// static +ExtensionSystem *ExtensionSystemFactoryQt::GetForBrowserContext(content::BrowserContext *context) +{ + return static_cast<ExtensionSystem *>(GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionSystemFactoryQt *ExtensionSystemFactoryQt::GetInstance() +{ + return base::Singleton<ExtensionSystemFactoryQt>::get(); +} + +ExtensionSystemFactoryQt::ExtensionSystemFactoryQt() + : ExtensionSystemProvider("ExtensionSystem", BrowserContextDependencyManager::GetInstance()) +{ + DCHECK(ExtensionsBrowserClient::Get()) << "ExtensionSystemFactory must be initialized after BrowserProcess"; + DependsOn(ExtensionPrefsFactory::GetInstance()); + DependsOn(ExtensionRegistryFactory::GetInstance()); +} + +ExtensionSystemFactoryQt::~ExtensionSystemFactoryQt() +{ +} + +KeyedService *ExtensionSystemFactoryQt::BuildServiceInstanceFor(content::BrowserContext *context) const +{ + return new ExtensionSystemQt(context); +} + +content::BrowserContext *ExtensionSystemFactoryQt::GetBrowserContextToUse(content::BrowserContext *context) const +{ + // Separate instance in incognito. + return context; +} + +bool ExtensionSystemFactoryQt::ServiceIsCreatedWithBrowserContext() const +{ + return true; +} + +} // namespace extensions diff --git a/src/core/extensions/extension_system_factory_qt.h b/src/core/extensions/extension_system_factory_qt.h new file mode 100644 index 000000000..6e840b6d6 --- /dev/null +++ b/src/core/extensions/extension_system_factory_qt.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSION_SYSTEM_FACTORY_QT_H_ +#define EXTENSION_SYSTEM_FACTORY_QT_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "extensions/browser/extension_system_provider.h" +#include "extension_system_qt.h" + +namespace extensions { +class ExtensionSystem; + +// BrowserContextKeyedServiceFactory for ExtensionSystemImpl. +// TODO(yoz): Rename to ExtensionSystemImplFactory. +class ExtensionSystemFactoryQt : public ExtensionSystemProvider +{ +public: + // ExtensionSystem provider implementation: + ExtensionSystem *GetForBrowserContext(content::BrowserContext *context) override; + + static ExtensionSystemFactoryQt *GetInstance(); + +private: + friend struct base::DefaultSingletonTraits<ExtensionSystemFactoryQt>; + + ExtensionSystemFactoryQt(); + ~ExtensionSystemFactoryQt() override; + + // BrowserContextKeyedServiceFactory implementation: + KeyedService *BuildServiceInstanceFor(content::BrowserContext *context) const override; + content::BrowserContext *GetBrowserContextToUse(content::BrowserContext *context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + + DISALLOW_COPY_AND_ASSIGN(ExtensionSystemFactoryQt); +}; + +} // namespace extensions + +#endif // EXTENSION_SYSTEM_FACTORY_QT_H_ diff --git a/src/core/extensions/extension_system_qt.cpp b/src/core/extensions/extension_system_qt.cpp new file mode 100644 index 000000000..4ca407421 --- /dev/null +++ b/src/core/extensions/extension_system_qt.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extension_system_qt.h" + +#include <algorithm> + +#include "base/base_paths.h" +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_string_value_serializer.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/path_service.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "build/build_config.h" +#include "components/crx_file/id_util.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/plugin_service.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/url_data_source.h" +#include "content/public/common/webplugininfo.h" +#include "extensions/browser/content_verifier.h" +#include "extensions/browser/content_verifier_delegate.h" +#include "extensions/browser/extension_pref_store.h" +#include "extensions/browser/extension_pref_value_map.h" +#include "extensions/browser/extension_pref_value_map_factory.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/info_map.h" +#include "extensions/browser/notification_types.h" +#include "extensions/browser/null_app_sorting.h" +#include "extensions/browser/quota_service.h" +#include "extensions/browser/renderer_startup_helper.h" +#include "extensions/browser/runtime_data.h" +#include "extensions/browser/shared_user_script_master.h" +#include "extensions/browser/service_worker_manager.h" +#include "extensions/browser/value_store/value_store_factory_impl.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension_messages.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" +#include "extensions/common/manifest_url_handlers.h" +#include "ui/base/resource/resource_bundle.h" +#include "chrome/grit/component_extension_resources.h" +#include "chrome/grit/browser_resources.h" +#include "net/base/mime_util.h" + +using content::BrowserThread; + +namespace extensions { + +namespace { + +std::string GenerateId(const base::DictionaryValue *manifest, + const base::FilePath &path) +{ + std::string raw_key; + std::string id_input; + CHECK(manifest->GetString(manifest_keys::kPublicKey, &raw_key)); + CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input)); + std::string id = crx_file::id_util::GenerateId(id_input); + return id; +} + +// Implementation based on ComponentLoader::ParseManifest. +std::unique_ptr<base::DictionaryValue> ParseManifest(const std::string &manifest_contents) +{ + JSONStringValueDeserializer deserializer(manifest_contents); + std::unique_ptr<base::Value> manifest(deserializer.Deserialize(NULL, NULL)); + + if (!manifest.get() || !manifest->is_dict()) { + LOG(ERROR) << "Failed to parse extension manifest."; + return NULL; + } + // Transfer ownership to the caller. + return base::DictionaryValue::From(std::move(manifest)); +} + +} // namespace + +// Dummy Content Verifier Delegate. Added to prevent crashes. +class ContentVerifierDelegateQt + : public ContentVerifierDelegate { + public: + ~ContentVerifierDelegateQt() override {} + + // This should return what verification mode is appropriate for the given + // extension, if any. + Mode ShouldBeVerified(const Extension& extension) override { + return NONE; + } + + // Should return the public key to use for validating signatures via the two + // out parameters. + ContentVerifierKey GetPublicKey() override { + return ContentVerifierKey(); + } + // This should return a URL that can be used to fetch the + // verified_contents.json containing signatures for the given extension + // id/version pair. + GURL GetSignatureFetchUrl(const std::string& extension_id, + const base::Version& version) override { + return GURL(); + } + + // This should return the set of file paths for images used within the + // browser process. (These may get transcoded during the install process). + std::set<base::FilePath> GetBrowserImagePaths( + const extensions::Extension* extension) override { + return std::set<base::FilePath>(); + } + + // Called when the content verifier detects that a read of a file inside + // an extension did not match its expected hash. + void VerifyFailed(const std::string& extension_id, + ContentVerifyJob::FailureReason reason) override { + + } + + // Called when ExtensionSystem is shutting down. + void Shutdown() override { + + } +}; + +void ExtensionSystemQt::LoadExtension(std::string extension_id, std::unique_ptr<base::DictionaryValue> manifest, const base::FilePath &directory) +{ + int flags = Extension::REQUIRE_KEY; + std::string error; + scoped_refptr<const Extension> extension = Extension::Create( + directory, + Manifest::COMPONENT, + *manifest, + flags, + &error); + if (!extension.get()) + LOG(ERROR) << error; + + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::Bind(&InfoMap::AddExtension, + base::Unretained(info_map()), + base::RetainedRef(extension), + base::Time::Now(), + true, + false)); + extension_registry_->AddEnabled(extension.get()); + + NotifyExtensionLoaded(extension.get()); +} + +void ExtensionSystemQt::OnExtensionRegisteredWithRequestContexts(scoped_refptr<const extensions::Extension> extension) +{ + extension_registry_->AddReady(extension); + if (extension_registry_->enabled_extensions().Contains(extension->id())) + extension_registry_->TriggerOnReady(extension.get()); +} + +// Implementation based on ExtensionService::NotifyExtensionLoaded. +void ExtensionSystemQt::NotifyExtensionLoaded(const Extension *extension) +{ + // The URLRequestContexts need to be first to know that the extension + // was loaded, otherwise a race can arise where a renderer that is created + // for the extension may try to load an extension URL with an extension id + // that the request context doesn't yet know about. The profile is responsible + // for ensuring its URLRequestContexts appropriately discover the loaded + // extension. + RegisterExtensionWithRequestContexts( + extension, + base::Bind(&ExtensionSystemQt::OnExtensionRegisteredWithRequestContexts, + weak_ptr_factory_.GetWeakPtr(), + base::WrapRefCounted(extension))); + + // Tell renderers about the loaded extension. + renderer_helper_->OnExtensionLoaded(*extension); + + // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded + // about the new extension. + // + // NOTE: It is important that this happen after notifying the renderers about + // the new extensions so that if we navigate to an extension URL in + // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to + // know about it. + extension_registry_->TriggerOnLoaded(extension); + + // Register plugins included with the extension. + // Implementation based on PluginManager::OnExtensionLoaded. + const MimeTypesHandler *handler = MimeTypesHandler::GetHandler(extension); + if (handler && !handler->handler_url().empty()) { + content::WebPluginInfo info; + info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN; + info.name = base::UTF8ToUTF16(extension->name()); + info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec()); + for (std::set<std::string>::const_iterator mime_type = handler->mime_type_set().begin(); + mime_type != handler->mime_type_set().end(); ++mime_type) { + content::WebPluginMimeType mime_type_info; + mime_type_info.mime_type = *mime_type; + base::FilePath::StringType file_extension; + if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) { + mime_type_info.file_extensions.push_back( + base::FilePath(file_extension).AsUTF8Unsafe()); + } + info.mime_types.push_back(mime_type_info); + } + content::PluginService *plugin_service = + content::PluginService::GetInstance(); + plugin_service->RefreshPlugins(); + plugin_service->RegisterInternalPlugin(info, true); + } +} + +bool ExtensionSystemQt::FinishDelayedInstallationIfReady(const std::string &extension_id, bool install_immediately) +{ + // TODO mibrunin + return false; +} + +void ExtensionSystemQt::Shutdown() +{ + if (content_verifier_.get()) + content_verifier_->Shutdown(); +} + +ServiceWorkerManager *ExtensionSystemQt::service_worker_manager() +{ + return service_worker_manager_.get(); +} + +ExtensionService *ExtensionSystemQt::extension_service() +{ + return nullptr; +} + +RuntimeData *ExtensionSystemQt::runtime_data() +{ + return runtime_data_.get(); +} + +ManagementPolicy *ExtensionSystemQt::management_policy() +{ + return nullptr; +} + +SharedUserScriptMaster *ExtensionSystemQt::shared_user_script_master() +{ + return shared_user_script_master_.get(); +} + +StateStore *ExtensionSystemQt::state_store() +{ + return nullptr; +} + +StateStore *ExtensionSystemQt::rules_store() +{ + return nullptr; +} + +scoped_refptr<ValueStoreFactory> ExtensionSystemQt::store_factory() +{ + return store_factory_; +} + +InfoMap *ExtensionSystemQt::info_map() +{ + if (!info_map_.get()) + info_map_ = new InfoMap; + return info_map_.get(); +} + +QuotaService *ExtensionSystemQt::quota_service() +{ + return quota_service_.get(); +} + +AppSorting *ExtensionSystemQt::app_sorting() +{ + return app_sorting_.get(); +} + +ContentVerifier *ExtensionSystemQt::content_verifier() +{ + if (!content_verifier_.get()) { + content_verifier_ = new ContentVerifier(browser_context_, std::make_unique<ContentVerifierDelegateQt>()); + } + return content_verifier_.get(); +} + +ExtensionSystemQt::ExtensionSystemQt(content::BrowserContext *browserContext) + : browser_context_(browserContext) + , store_factory_(new ValueStoreFactoryImpl(browserContext->GetPath())) + , extension_registry_(ExtensionRegistry::Get(browserContext)) + , renderer_helper_(extensions::RendererStartupHelperFactory::GetForBrowserContext(browserContext)) + , initialized_(false) + , weak_ptr_factory_(this) +{ +} + +ExtensionSystemQt::~ExtensionSystemQt() +{ +} + +void ExtensionSystemQt::Init(bool extensions_enabled) +{ + if (initialized_) + return; + + initialized_ = true; + + service_worker_manager_.reset(new ServiceWorkerManager(browser_context_)); + runtime_data_.reset(new RuntimeData(extension_registry_)); + quota_service_.reset(new QuotaService); + app_sorting_.reset(new NullAppSorting); + + shared_user_script_master_ = + std::make_unique<SharedUserScriptMaster>(browser_context_); + + // Make the chrome://extension-icon/ resource available. + // content::URLDataSource::Add(browser_context_, new ExtensionIconSource(browser_context_)); + + if (extensions_enabled) { + // Inform the rest of the extensions system to start. + ready_.Signal(); + content::NotificationService::current()->Notify( + NOTIFICATION_EXTENSIONS_READY_DEPRECATED, + content::Source<content::BrowserContext>(browser_context_), + content::NotificationService::NoDetails()); + + std::string pdf_manifest = ui::ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_PDF_MANIFEST).as_string(); + base::ReplaceFirstSubstringAfterOffset(&pdf_manifest, 0, "<NAME>", "chromium-pdf"); + + std::unique_ptr<base::DictionaryValue> pdfManifestDict = ParseManifest(pdf_manifest); + base::FilePath path; + base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &path); + path = path.Append(base::FilePath(FILE_PATH_LITERAL("pdf"))); + std::string id = GenerateId(pdfManifestDict.get(), path); + LoadExtension(id, std::move(pdfManifestDict), path); + } +} + +void ExtensionSystemQt::InitForRegularProfile(bool extensions_enabled) +{ + if (initialized_) + return; // Already initialized. + // The InfoMap needs to be created before the ProcessManager. + info_map(); + + Init(extensions_enabled); +} + +void ExtensionSystemQt::InitForIncognitoProfile() +{ + NOTIMPLEMENTED(); +} + +std::unique_ptr<ExtensionSet> ExtensionSystemQt::GetDependentExtensions(const Extension *extension) +{ + return base::WrapUnique(new ExtensionSet()); +} + +#if !defined(TOOLKIT_QT) +void ExtensionSystemQt::InstallUpdate(const std::string &extension_id, + const std::string &public_key, + const base::FilePath &unpacked_dir, + bool install_immediately, + InstallUpdateCallback install_update_callback) +{ + NOTREACHED() << "Not yet implemented"; + base::DeleteFile(unpacked_dir, true /* recursive */); + std::move(install_update_callback).Run(CrxInstallError(CrxInstallErrorType::DECLINED, CrxInstallErrorDetail::DISALLOWED_BY_POLICY)); +} +#endif + +void ExtensionSystemQt::RegisterExtensionWithRequestContexts(const Extension *extension, + const base::Closure &callback) +{ + base::Time install_time = base::Time::Now(); + + bool incognito_enabled = false; + bool notifications_disabled = false; + + base::PostTaskWithTraitsAndReply( + FROM_HERE, {BrowserThread::IO}, + base::Bind(&InfoMap::AddExtension, info_map(), + base::RetainedRef(extension), install_time, incognito_enabled, + notifications_disabled), + callback); +} + +void ExtensionSystemQt::UnregisterExtensionWithRequestContexts(const std::string &extension_id, + const UnloadedExtensionReason reason) +{ + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::IO}, + base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason)); +} +} // namespace extensions diff --git a/src/core/extensions/extension_system_qt.h b/src/core/extensions/extension_system_qt.h new file mode 100644 index 000000000..0ebe1d044 --- /dev/null +++ b/src/core/extensions/extension_system_qt.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Large parts of this file are based on the source code from the file +// chrome/browser/extensions/extension_system_impl.h from the Chromium sources. + +#ifndef EXTENSION_SYSTEM_QT_H +#define EXTENSION_SYSTEM_QT_H + +#include <string> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "build/build_config.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/extension_set.h" +#include "extensions/common/one_shot_event.h" + +namespace extensions { + +class ExtensionRegistry; +class InfoMap; +class RendererStartupHelper; +class ServiceWorkerManager; +class StateStoreNotificationObserver; +class ValueStoreFactory; +class ValueStoreFactoryImpl; + +// The ExtensionSystem for ProfileImpl and OffTheRecordProfileImpl. +// Implementation details: non-shared services are owned by +// ExtensionSystemImpl, a KeyedService with separate incognito +// instances. A private Shared class (also a KeyedService, +// but with a shared instance for incognito) keeps the common services. +class ExtensionSystemQt : public ExtensionSystem +{ +public: + explicit ExtensionSystemQt(content::BrowserContext *browserContext); + ~ExtensionSystemQt() override; + + // Initializes the extension system. + void Initialize(); + + // KeyedService implementation: + void Shutdown() override; + + // ExtensionSystem implementation: + void InitForRegularProfile(bool extensions_enabled) override; + void InitForIncognitoProfile() override; + ExtensionService *extension_service() override; + RuntimeData *runtime_data() override; + ManagementPolicy *management_policy() override; + ServiceWorkerManager *service_worker_manager() override; + SharedUserScriptMaster *shared_user_script_master() override; + StateStore* state_store() override; + StateStore* rules_store() override; + scoped_refptr<ValueStoreFactory> store_factory() override; + InfoMap *info_map() override; + QuotaService *quota_service() override; + AppSorting *app_sorting() override; + + void RegisterExtensionWithRequestContexts(const Extension *extension, + const base::Closure &callback) override; + + void UnregisterExtensionWithRequestContexts(const std::string &extension_id, + const UnloadedExtensionReason reason) override; + + ContentVerifier *content_verifier() override; + std::unique_ptr<ExtensionSet> GetDependentExtensions(const Extension *extension) override; + +#if !defined(TOOLKIT_QT) + void InstallUpdate(const std::string &extension_id, + const std::string &public_key, + const base::FilePath &unpacked_dir, + bool install_immediately, + InstallUpdateCallback install_update_callback) override; +#endif // TOOLKIT_QT + //friend class ExtensionSystemSharedFactory; + + bool FinishDelayedInstallationIfReady(const std::string &extension_id, bool install_immediately) override; + + void Init(bool extensions_enabled); + + const OneShotEvent &ready() const override { return ready_; } + +private: + void OnExtensionRegisteredWithRequestContexts(scoped_refptr<const extensions::Extension> extension); + + void NotifyExtensionLoaded(const Extension *extension); + void LoadExtension(std::string extension_id, std::unique_ptr<base::DictionaryValue> manifest, const base::FilePath &directory); + // The services that are shared between normal and incognito profiles. + + // Data to be accessed on the IO thread. Must outlive process_manager_. + scoped_refptr<InfoMap> info_map_; + + std::unique_ptr<ServiceWorkerManager> service_worker_manager_; + std::unique_ptr<RuntimeData> runtime_data_; + std::unique_ptr<QuotaService> quota_service_; + std::unique_ptr<AppSorting> app_sorting_; + std::unique_ptr<SharedUserScriptMaster> shared_user_script_master_; + + + // For verifying the contents of extensions read from disk. + scoped_refptr<ContentVerifier> content_verifier_; + OneShotEvent ready_; + + content::BrowserContext *browser_context_; + scoped_refptr<ValueStoreFactory> store_factory_; + ExtensionRegistry *extension_registry_; + extensions::RendererStartupHelper *renderer_helper_; + bool initialized_; + + base::WeakPtrFactory<ExtensionSystemQt> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ExtensionSystemQt); +}; + +} // namespace extensions + +#endif // EXTENSION_SYSTEM_QT_H diff --git a/src/core/extensions/extension_web_contents_observer_qt.cpp b/src/core/extensions/extension_web_contents_observer_qt.cpp new file mode 100644 index 000000000..1eb2298ca --- /dev/null +++ b/src/core/extensions/extension_web_contents_observer_qt.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extension_web_contents_observer_qt.h" + +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/url_constants.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/common/manifest.h" + +namespace extensions { + +ExtensionWebContentsObserverQt::ExtensionWebContentsObserverQt(content::WebContents *web_contents) + : ExtensionWebContentsObserver(web_contents) +{ +} + +ExtensionWebContentsObserverQt::~ExtensionWebContentsObserverQt() +{ +} + +// static +void ExtensionWebContentsObserverQt::CreateForWebContents(content::WebContents *web_contents) +{ + content::WebContentsUserData<ExtensionWebContentsObserverQt>::CreateForWebContents(web_contents); + + // Initialize this instance if necessary. + FromWebContents(web_contents)->Initialize(); +} + +std::string ExtensionWebContentsObserverQt::GetExtensionIdFromFrame(content::RenderFrameHost *render_frame_host) const +{ + const GURL &site = render_frame_host->GetSiteInstance()->GetSiteURL(); + if (!site.SchemeIs(kExtensionScheme)) + return std::string(); + + return site.host(); +} + +const Extension *ExtensionWebContentsObserverQt::GetExtensionFromFrame(content::RenderFrameHost *render_frame_host, bool verify_url) const +{ + std::string extension_id = GetExtensionIdFromFrame(render_frame_host); + if (extension_id.empty()) + return nullptr; + + content::BrowserContext *browser_context = + render_frame_host->GetProcess()->GetBrowserContext(); + const Extension *extension = ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetByID(extension_id); + if (!extension) + return nullptr; + + if (verify_url) { + const url::Origin &origin(render_frame_host->GetLastCommittedOrigin()); + // Without site isolation, this check is needed to eliminate non-extension + // schemes. With site isolation, this is still needed to exclude sandboxed + // extension frames with a unique origin. + const GURL site_url(render_frame_host->GetSiteInstance()->GetSiteURL()); + if (origin.opaque() || site_url != content::SiteInstance::GetSiteForURL(browser_context, origin.GetURL())) + return nullptr; + } + + return extension; +} + +void ExtensionWebContentsObserverQt::RenderFrameCreated(content::RenderFrameHost *render_frame_host) +{ + ExtensionWebContentsObserver::RenderFrameCreated(render_frame_host); + + const Extension *extension = GetExtensionFromFrame(render_frame_host, false); + if (!extension) + return; + + int process_id = render_frame_host->GetProcess()->GetID(); + auto *policy = content::ChildProcessSecurityPolicy::GetInstance(); + + if (extension->is_extension() && Manifest::IsComponentLocation(extension->location())) + policy->GrantRequestOrigin(process_id, url::Origin::Create(GURL(content::kChromeUIResourcesURL))); +} + +} // namespace extensions diff --git a/src/core/extensions/extension_web_contents_observer_qt.h b/src/core/extensions/extension_web_contents_observer_qt.h new file mode 100644 index 000000000..043b9d4fa --- /dev/null +++ b/src/core/extensions/extension_web_contents_observer_qt.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSION_WEB_CONTENTS_OBSERVER_QT_H_ +#define EXTENSION_WEB_CONTENTS_OBSERVER_QT_H_ + +#include "content/public/browser/web_contents_user_data.h" +#include "extensions/browser/extension_web_contents_observer.h" + +namespace extensions { + +class ExtensionWebContentsObserverQt + : public ExtensionWebContentsObserver, + public content::WebContentsUserData<ExtensionWebContentsObserverQt> +{ +public: + explicit ExtensionWebContentsObserverQt(content::WebContents *web_contents); + ~ExtensionWebContentsObserverQt() override; + + static void CreateForWebContents(content::WebContents *web_contents); + + std::string GetExtensionIdFromFrame(content::RenderFrameHost *) const; + const Extension *GetExtensionFromFrame(content::RenderFrameHost *, bool) const; + + // content::WebContentsObserver overrides. + void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override; + +private: + friend class content::WebContentsUserData<ExtensionWebContentsObserverQt>; + DISALLOW_COPY_AND_ASSIGN(ExtensionWebContentsObserverQt); +}; + +} // namespace extensions + +#endif // EXTENSION_WEB_CONTENTS_OBSERVER_QT_H_ diff --git a/src/core/extensions/extensions_api_client_qt.cpp b/src/core/extensions/extensions_api_client_qt.cpp new file mode 100644 index 000000000..731b79a63 --- /dev/null +++ b/src/core/extensions/extensions_api_client_qt.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions_api_client_qt.h" + +#include <memory> +//#include "base/memory/ptr_util.h" +#include "extension_web_contents_observer_qt.h" +#include "components/pdf/browser/pdf_web_contents_helper.h" +#include "extensions/browser/guest_view/extensions_guest_view_manager_delegate.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h" +#include "printing/print_view_manager_qt.h" + +namespace extensions { + +ExtensionsAPIClientQt::ExtensionsAPIClientQt() +{ +} + +AppViewGuestDelegate *ExtensionsAPIClientQt::CreateAppViewGuestDelegate() const +{ + // TODO(extensions): Implement to support Apps. + NOTREACHED(); + return nullptr; +} + +std::unique_ptr<guest_view::GuestViewManagerDelegate> ExtensionsAPIClientQt::CreateGuestViewManagerDelegate(content::BrowserContext *context) const +{ + return std::make_unique<guest_view::GuestViewManagerDelegate>(); +} + +std::unique_ptr<MimeHandlerViewGuestDelegate> ExtensionsAPIClientQt::CreateMimeHandlerViewGuestDelegate(MimeHandlerViewGuest *guest) const +{ + return std::make_unique<MimeHandlerViewGuestDelegate>(); +} + +void ExtensionsAPIClientQt::AttachWebContentsHelpers(content::WebContents *web_contents) const +{ + // PrefsTabHelper::CreateForWebContents(web_contents); + QtWebEngineCore::PrintViewManagerQt::CreateForWebContents(web_contents); + ExtensionWebContentsObserverQt::CreateForWebContents(web_contents); +} + +} // namespace extensions diff --git a/src/core/extensions/extensions_api_client_qt.h b/src/core/extensions/extensions_api_client_qt.h new file mode 100644 index 000000000..2fa69f539 --- /dev/null +++ b/src/core/extensions/extensions_api_client_qt.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_API_CLIENT_QT_H_ +#define EXTENSIONS_API_CLIENT_QT_H_ + +#include "extensions/browser/api/extensions_api_client.h" + +namespace extensions { + +class ExtensionsAPIClientQt : public ExtensionsAPIClient +{ +public: + ExtensionsAPIClientQt(); + + // ExtensionsAPIClient implementation. + AppViewGuestDelegate *CreateAppViewGuestDelegate() const override; + std::unique_ptr<guest_view::GuestViewManagerDelegate> + CreateGuestViewManagerDelegate(content::BrowserContext *context) const override; + std::unique_ptr<MimeHandlerViewGuestDelegate> + CreateMimeHandlerViewGuestDelegate(MimeHandlerViewGuest *guest) const override; + void AttachWebContentsHelpers(content::WebContents *web_contents) const override; +}; + +} // namespace extensions + +#endif // EXTENSIONS_API_CLIENT_QT_H_ diff --git a/src/core/extensions/extensions_browser_api_provider_qt.cpp b/src/core/extensions/extensions_browser_api_provider_qt.cpp new file mode 100644 index 000000000..cc1932c64 --- /dev/null +++ b/src/core/extensions/extensions_browser_api_provider_qt.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** 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 "extensions_browser_api_provider_qt.h" + +#include "extensions/browser/api/generated_api_registration.h" + +namespace extensions { +ExtensionsBrowserAPIProviderQt::ExtensionsBrowserAPIProviderQt() = + default; +ExtensionsBrowserAPIProviderQt::~ExtensionsBrowserAPIProviderQt() = + default; + +void ExtensionsBrowserAPIProviderQt::RegisterExtensionFunctions( + ExtensionFunctionRegistry* registry) { + api::GeneratedFunctionRegistry::RegisterAll(registry); +} + + +} + diff --git a/src/core/extensions/extensions_browser_api_provider_qt.h b/src/core/extensions/extensions_browser_api_provider_qt.h new file mode 100644 index 000000000..612df3825 --- /dev/null +++ b/src/core/extensions/extensions_browser_api_provider_qt.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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 EXTENSIONS_API_PROVIDER_QT_H +#define EXTENSIONS_API_PROVIDER_QT_H + +#include "extensions/browser/extensions_browser_api_provider.h" +#include "base/macros.h" + +namespace extensions { + +class ExtensionsBrowserAPIProviderQt : public ExtensionsBrowserAPIProvider { +public: + ExtensionsBrowserAPIProviderQt(); + ~ExtensionsBrowserAPIProviderQt() override; + + void RegisterExtensionFunctions(ExtensionFunctionRegistry *registry) override; + +private: + DISALLOW_COPY_AND_ASSIGN(ExtensionsBrowserAPIProviderQt); +}; + +} + +#endif // EXTENSIONS_API_PROVIDER_QT_H diff --git a/src/core/extensions/extensions_browser_client_qt.cpp b/src/core/extensions/extensions_browser_client_qt.cpp new file mode 100644 index 000000000..8bba4128f --- /dev/null +++ b/src/core/extensions/extensions_browser_client_qt.cpp @@ -0,0 +1,499 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions_browser_client_qt.h" + +#include <utility> + +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "base/memory/ref_counted_memory.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/api/runtime/runtime_api_delegate.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/core_extensions_browser_api_provider.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_host_delegate.h" +#include "extensions/browser/extension_protocols.h" +#include "extensions/browser/mojo/interface_registration.h" +#include "extensions/browser/url_request_util.h" +#include "extensions/common/file_util.h" +#include "net/base/completion_once_callback.h" +#include "net/base/mime_util.h" +#include "net/url_request/url_request_simple_job.h" +#include "ui/base/resource/resource_bundle.h" + +#include "component_extension_resource_manager_qt.h" +#include "extension_system_factory_qt.h" +#include "extension_web_contents_observer_qt.h" +#include "extensions_api_client_qt.h" +#include "extensions_browser_api_provider_qt.h" +#include "extensions_browser_client_qt.h" +#include "web_engine_library_info.h" + +using content::BrowserContext; +using content::BrowserThread; + +namespace { + +// helpers based on implementation in chrome_url_request_util.cc: +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void DetermineCharset(const std::string &mime_type, + const base::RefCountedMemory *data, + std::string *out_charset) +{ + if (base::StartsWith(mime_type, "text/", base::CompareCase::INSENSITIVE_ASCII)) { + // All of our HTML files should be UTF-8 and for other resource types + // (like images), charset doesn't matter. + DCHECK(base::IsStringUTF8(base::StringPiece(reinterpret_cast<const char*>(data->front()), data->size()))); + *out_charset = "utf-8"; + } +} + +// A request for an extension resource in a Chrome .pak file. These are used +// by component extensions. +class URLRequestResourceBundleJob : public net::URLRequestSimpleJob { +public: + URLRequestResourceBundleJob(net::URLRequest *request, + net::NetworkDelegate *network_delegate, + const base::FilePath &filename, + int resource_id, + const std::string &content_security_policy, + bool send_cors_header) + : net::URLRequestSimpleJob(request, network_delegate) + , filename_(filename) + , resource_id_(resource_id) + , weak_factory_(this) + { + // Leave cache headers out of resource bundle requests. + response_info_.headers = extensions::BuildHttpHeaders(content_security_policy, send_cors_header, base::Time()); + } + int GetRefCountedData(std::string* mime_type, + std::string* charset, + scoped_refptr<base::RefCountedMemory>* data, + net::CompletionOnceCallback callback) const override + { + const ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + *data = rb.LoadDataResourceBytes(resource_id_); + + // Add the Content-Length header now that we know the resource length. + response_info_.headers->AddHeader( + base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentLength, + base::NumberToString((*data)->size()).c_str())); + + std::string* read_mime_type = new std::string; + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&net::GetMimeTypeFromFile, filename_, + base::Unretained(read_mime_type)), + base::BindOnce(&URLRequestResourceBundleJob::OnMimeTypeRead, + weak_factory_.GetWeakPtr(), mime_type, charset, *data, + base::Owned(read_mime_type), std::move(callback))); + + return net::ERR_IO_PENDING; + } + + void GetResponseInfo(net::HttpResponseInfo* info) override + { + *info = response_info_; + } + +private: + ~URLRequestResourceBundleJob() override {} + + void OnMimeTypeRead(std::string *out_mime_type, + std::string *charset, + scoped_refptr<base::RefCountedMemory> data, + std::string *read_mime_type, + net::CompletionOnceCallback callback, + bool read_result) + { + response_info_.headers->AddHeader( + base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType, + read_mime_type->c_str())); + *out_mime_type = *read_mime_type; + DetermineCharset(*read_mime_type, data.get(), charset); + int result = read_result ? net::OK : net::ERR_INVALID_URL; + std::move(callback).Run(result); + } + + // We need the filename of the resource to determine the mime type. + base::FilePath filename_; + + // The resource bundle id to load. + int resource_id_; + + net::HttpResponseInfo response_info_; + + mutable base::WeakPtrFactory<URLRequestResourceBundleJob> weak_factory_; +}; + +} // namespace + +namespace extensions { + +ExtensionsBrowserClientQt::ExtensionsBrowserClientQt() + : api_client_(new ExtensionsAPIClientQt) + , resource_manager_(new ComponentExtensionResourceManagerQt) +{ + AddAPIProvider(std::make_unique<CoreExtensionsBrowserAPIProvider>()); + AddAPIProvider(std::make_unique<ExtensionsBrowserAPIProviderQt>()); +} + +ExtensionsBrowserClientQt::~ExtensionsBrowserClientQt() +{ +} + +bool ExtensionsBrowserClientQt::IsShuttingDown() +{ + return false; +} + +bool ExtensionsBrowserClientQt::AreExtensionsDisabled(const base::CommandLine &command_line, BrowserContext *context) +{ + return false; +} + +bool ExtensionsBrowserClientQt::IsValidContext(BrowserContext *context) +{ + return true; +} + +bool ExtensionsBrowserClientQt::IsSameContext(BrowserContext *first, + BrowserContext *second) +{ + return first == second; +} + +bool ExtensionsBrowserClientQt::HasOffTheRecordContext(BrowserContext *context) +{ + return false; +} + +BrowserContext *ExtensionsBrowserClientQt::GetOffTheRecordContext(BrowserContext *context) +{ + // TODO(extensions): Do we need to support this? + return nullptr; +} + +BrowserContext *ExtensionsBrowserClientQt::GetOriginalContext(BrowserContext *context) +{ + return context; +} + +bool ExtensionsBrowserClientQt::IsGuestSession(BrowserContext *context) const +{ + return false; +} + +bool ExtensionsBrowserClientQt::IsExtensionIncognitoEnabled(const std::string &extension_id, + content::BrowserContext *context) const +{ + return false; +} + +bool ExtensionsBrowserClientQt::CanExtensionCrossIncognito(const Extension *extension, + content::BrowserContext *context) const +{ + return false; +} + +net::URLRequestJob *ExtensionsBrowserClientQt::MaybeCreateResourceBundleRequestJob(net::URLRequest *request, + net::NetworkDelegate *network_delegate, + const base::FilePath &directory_path, + const std::string &content_security_policy, + bool send_cors_header) +{ + base::FilePath resources_path; + base::FilePath relative_path; + // Try to load extension resources from chrome resource file if + // directory_path is a descendant of resources_path. resources_path + // corresponds to src/chrome/browser/resources in source tree. + if (base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &resources_path) && + // Since component extension resources are included in + // component_extension_resources.pak file in resources_path, calculate + // extension relative path against resources_path. + resources_path.AppendRelativePath(directory_path, &relative_path)) { + base::FilePath request_path = extensions::file_util::ExtensionURLToRelativeFilePath(request->url()); + int resource_id = 0; + if (GetComponentExtensionResourceManager()->IsComponentExtensionResource(directory_path, request_path, &resource_id)) { + relative_path = relative_path.Append(request_path); + relative_path = relative_path.NormalizePathSeparators(); + return new URLRequestResourceBundleJob(request, + network_delegate, + relative_path, + resource_id, + content_security_policy, + send_cors_header); + } + } + return nullptr; +} + +// Return the resource relative path and id for the given request. +base::FilePath ExtensionsBrowserClientQt::GetBundleResourcePath(const network::ResourceRequest &request, + const base::FilePath &extension_resources_path, + int *resource_id) const +{ + *resource_id = 0; + // |chrome_resources_path| corresponds to src/chrome/browser/resources in + // source tree. + base::FilePath resources_path; + if (!base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &resources_path)) + return base::FilePath(); + + // Since component extension resources are included in + // component_extension_resources.pak file in |chrome_resources_path|, + // calculate the extension |request_relative_path| against + // |chrome_resources_path|. + if (!resources_path.IsParent(extension_resources_path)) + return base::FilePath(); + + const base::FilePath request_relative_path = + extensions::file_util::ExtensionURLToRelativeFilePath(request.url); + if (!ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()->IsComponentExtensionResource( + extension_resources_path, request_relative_path, resource_id)) { + return base::FilePath(); + } + DCHECK_NE(0, *resource_id); + + return request_relative_path; +} + +// Creates and starts a URLLoader to load an extension resource from the +// embedder's resource bundle (.pak) files. Used for component extensions. +void ExtensionsBrowserClientQt::LoadResourceFromResourceBundle(const network::ResourceRequest &request, + network::mojom::URLLoaderRequest loader, + const base::FilePath &resource_relative_path, + int resource_id, + const std::string &content_security_policy, + network::mojom::URLLoaderClientPtr client, + bool send_cors_header) +{ + NOTIMPLEMENTED(); +} + + +bool ExtensionsBrowserClientQt::AllowCrossRendererResourceLoad(const GURL &url, + content::ResourceType resource_type, + ui::PageTransition page_transition, + int child_id, + bool is_incognito, + const Extension *extension, + const ExtensionSet &extensions, + const ProcessMap &process_map) +{ + + if (extension && extension->id() == extension_misc::kPdfExtensionId) + return true; + + bool allowed = false; + if (url_request_util::AllowCrossRendererResourceLoad(url, resource_type, + page_transition, child_id, + is_incognito, extension, extensions, + process_map, &allowed)) { + return allowed; + } + // Couldn't determine if resource is allowed. Block the load. + return false; +} + +PrefService *ExtensionsBrowserClientQt::GetPrefServiceForContext(BrowserContext *context) +{ + return static_cast<Profile *>(context)->GetPrefs(); +} + +void ExtensionsBrowserClientQt::GetEarlyExtensionPrefsObservers(content::BrowserContext *context, + std::vector<ExtensionPrefsObserver *> *observers) const +{ +} + +ProcessManagerDelegate *ExtensionsBrowserClientQt::GetProcessManagerDelegate() const +{ + return nullptr; +} + +std::unique_ptr<ExtensionHostDelegate> ExtensionsBrowserClientQt::CreateExtensionHostDelegate() +{ + // TODO(extensions): Implement to support Apps. + NOTREACHED(); + return std::unique_ptr<ExtensionHostDelegate>(); +} + +bool ExtensionsBrowserClientQt::DidVersionUpdate(BrowserContext *context) +{ + // TODO(jamescook): We might want to tell extensions when app_shell updates. + return false; +} + +void ExtensionsBrowserClientQt::PermitExternalProtocolHandler() +{ +} + +bool ExtensionsBrowserClientQt::IsRunningInForcedAppMode() +{ + return false; +} + +bool ExtensionsBrowserClientQt::IsLoggedInAsPublicAccount() +{ + return false; +} + +ExtensionSystemProvider *ExtensionsBrowserClientQt::GetExtensionSystemFactory() +{ + return ExtensionSystemFactoryQt::GetInstance(); +} + +// void ExtensionsBrowserClientQt::RegisterExtensionFunctions(ExtensionFunctionRegistry *registry) const +//{ +// // Register core extension-system APIs. +// api::GeneratedFunctionRegistry::RegisterAll(registry); +//} + +void ExtensionsBrowserClientQt::RegisterExtensionInterfaces(service_manager::BinderRegistryWithArgs<content::RenderFrameHost *> *registry, + content::RenderFrameHost *render_frame_host, + const Extension *extension) const +{ + RegisterInterfacesForExtension(registry, render_frame_host, extension); +} + +std::unique_ptr<RuntimeAPIDelegate> ExtensionsBrowserClientQt::CreateRuntimeAPIDelegate(content::BrowserContext *context) const +{ + // TODO(extensions): Implement to support Apps. + NOTREACHED(); + return std::unique_ptr<RuntimeAPIDelegate>(); +} + +const ComponentExtensionResourceManager *ExtensionsBrowserClientQt::GetComponentExtensionResourceManager() +{ + return resource_manager_.get(); +} + +void ExtensionsBrowserClientQt::BroadcastEventToRenderers(events::HistogramValue histogram_value, + const std::string &event_name, + std::unique_ptr<base::ListValue> args) +{ + NOTIMPLEMENTED(); + // TODO : do the event routing + // event_router_forwarder_->BroadcastEventToRenderers( + // histogram_value, event_name, std::move(args), GURL()); +} + +net::NetLog *ExtensionsBrowserClientQt::GetNetLog() +{ + return nullptr; +} + +ExtensionCache *ExtensionsBrowserClientQt::GetExtensionCache() +{ + // Only used by Chrome via ExtensionService. + NOTREACHED(); + return nullptr; +} + +bool ExtensionsBrowserClientQt::IsBackgroundUpdateAllowed() +{ + return true; +} + +bool ExtensionsBrowserClientQt::IsMinBrowserVersionSupported( + const std::string &min_version) +{ + return true; +} + +bool ExtensionsBrowserClientQt::IsLockScreenContext(content::BrowserContext *context) +{ + return false; +} + +// Returns the locale used by the application. +std::string ExtensionsBrowserClientQt::GetApplicationLocale() +{ + return WebEngineLibraryInfo::getApplicationLocale(); +} + +bool ExtensionsBrowserClientQt::IsAppModeForcedForApp(const ExtensionId &id) +{ + return false; +} + +bool ExtensionsBrowserClientQt::IsInDemoMode() +{ + return false; +} + +ExtensionWebContentsObserver *ExtensionsBrowserClientQt::GetExtensionWebContentsObserver(content::WebContents *web_contents) +{ + return ExtensionWebContentsObserverQt::FromWebContents(web_contents); +} + +KioskDelegate *ExtensionsBrowserClientQt::GetKioskDelegate() +{ + NOTREACHED(); + return nullptr; +} + +bool ExtensionsBrowserClientQt::IsScreensaverInDemoMode(const std::string& app_id) +{ + return false; +} + +void ExtensionsBrowserClientQt::SetAPIClientForTest(ExtensionsAPIClient *api_client) +{ + api_client_.reset(api_client); +} + +} // namespace extensions diff --git a/src/core/extensions/extensions_browser_client_qt.h b/src/core/extensions/extensions_browser_client_qt.h new file mode 100644 index 000000000..f766b96a7 --- /dev/null +++ b/src/core/extensions/extensions_browser_client_qt.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_CLIENT_QT_H_ +#define EXTENSIONS_BROWSER_CLIENT_QT_H_ + +#include "base/compiler_specific.h" +#include "extensions/browser/extensions_browser_client.h" + +namespace extensions { + +class ExtensionsAPIClient; + +// An ExtensionsBrowserClient that supports a single content::BrowserContent +// with no related incognito context. +class ExtensionsBrowserClientQt : public ExtensionsBrowserClient +{ +public: + ExtensionsBrowserClientQt(); + ~ExtensionsBrowserClientQt() override; + + // ExtensionsBrowserClient overrides: + bool IsShuttingDown() override; + bool AreExtensionsDisabled(const base::CommandLine &command_line, + content::BrowserContext *context) override; + bool IsValidContext(content::BrowserContext *context) override; + bool IsSameContext(content::BrowserContext *first, + content::BrowserContext *second) override; + bool HasOffTheRecordContext(content::BrowserContext *context) override; + content::BrowserContext *GetOffTheRecordContext(content::BrowserContext *context) override; + content::BrowserContext *GetOriginalContext(content::BrowserContext *context) override; + bool IsGuestSession(content::BrowserContext *context) const override; + bool IsExtensionIncognitoEnabled(const std::string &extension_id, content::BrowserContext *context) const override; + bool CanExtensionCrossIncognito(const Extension *extension, content::BrowserContext *context) const override; + net::URLRequestJob *MaybeCreateResourceBundleRequestJob(net::URLRequest *request, + net::NetworkDelegate *network_delegate, + const base::FilePath &directory_path, + const std::string &content_security_policy, + bool send_cors_header) override; + bool AllowCrossRendererResourceLoad(const GURL &url, + content::ResourceType resource_type, + ui::PageTransition page_transition, + int child_id, + bool is_incognito, + const Extension *extension, + const ExtensionSet &extensions, + const ProcessMap &process_map) override; + PrefService *GetPrefServiceForContext(content::BrowserContext *context) override; + void GetEarlyExtensionPrefsObservers(content::BrowserContext *context, std::vector<ExtensionPrefsObserver *> *observers) const + override; + ProcessManagerDelegate *GetProcessManagerDelegate() const override; + std::unique_ptr<ExtensionHostDelegate> + CreateExtensionHostDelegate() override; + bool DidVersionUpdate(content::BrowserContext *context) override; + void PermitExternalProtocolHandler() override; + bool IsRunningInForcedAppMode() override; + bool IsLoggedInAsPublicAccount() override; + ExtensionSystemProvider *GetExtensionSystemFactory() override; +// void RegisterExtensionFunctions(ExtensionFunctionRegistry *registry) const; + std::unique_ptr<RuntimeAPIDelegate> CreateRuntimeAPIDelegate(content::BrowserContext *context) const override; + void RegisterExtensionInterfaces(service_manager::BinderRegistryWithArgs<content::RenderFrameHost *> *registry, + content::RenderFrameHost *render_frame_host, + const Extension *extension) const override; + const ComponentExtensionResourceManager * + GetComponentExtensionResourceManager() override; + void BroadcastEventToRenderers(events::HistogramValue histogram_value, + const std::string &event_name, + std::unique_ptr<base::ListValue> args) override; + net::NetLog *GetNetLog() override; + ExtensionCache *GetExtensionCache() override; + bool IsBackgroundUpdateAllowed() override; + bool IsMinBrowserVersionSupported(const std::string &min_version) override; + ExtensionWebContentsObserver *GetExtensionWebContentsObserver( + content::WebContents *web_contents) override; + KioskDelegate *GetKioskDelegate() override; + + // Whether the browser context is associated with Chrome OS lock screen. + bool IsLockScreenContext(content::BrowserContext *context) override; + + bool IsAppModeForcedForApp(const ExtensionId &id) override; + bool IsInDemoMode() override; + + // Return the resource relative path and id for the given request. + base::FilePath GetBundleResourcePath(const network::ResourceRequest &request, + const base::FilePath &extension_resources_path, + int *resource_id) const override; + + // Creates and starts a URLLoader to load an extension resource from the + // embedder's resource bundle (.pak) files. Used for component extensions. + void LoadResourceFromResourceBundle(const network::ResourceRequest &request, + network::mojom::URLLoaderRequest loader, + const base::FilePath &resource_relative_path, + int resource_id, + const std::string &content_security_policy, + network::mojom::URLLoaderClientPtr client, + bool send_cors_header) override; + + // Returns the locale used by the application. + std::string GetApplicationLocale() override; + + bool IsScreensaverInDemoMode(const std::string& app_id) override; + + // Sets the API client. + void SetAPIClientForTest(ExtensionsAPIClient *api_client); + +private: + // Support for extension APIs. + std::unique_ptr<ExtensionsAPIClient> api_client_; + + // Resource manager used to supply resources from pak files. + std::unique_ptr<ComponentExtensionResourceManager> resource_manager_; + + //scoped_refptr<EventRouterForwarder> event_router_forwarder_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionsBrowserClientQt); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_CLIENT_QT_H_ diff --git a/src/core/extensions/mime_handler_view_guest_delegate_qt.cpp b/src/core/extensions/mime_handler_view_guest_delegate_qt.cpp new file mode 100644 index 000000000..438b8a83e --- /dev/null +++ b/src/core/extensions/mime_handler_view_guest_delegate_qt.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mime_handler_view_guest_delegate_qt.h" + +#include "content/browser/browser_plugin/browser_plugin_guest.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/context_menu_params.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" + +namespace extensions { + +MimeHandlerViewGuestDelegateQt::MimeHandlerViewGuestDelegateQt(MimeHandlerViewGuest *guest) + : MimeHandlerViewGuestDelegate() +{ +} + +MimeHandlerViewGuestDelegateQt::~MimeHandlerViewGuestDelegateQt() +{ +} + +bool MimeHandlerViewGuestDelegateQt::HandleContextMenu(content::WebContents *web_contents, const content::ContextMenuParams ¶ms) +{ + content::ContextMenuParams new_params = params; + + gfx::Point guest_coordinates = + static_cast<content::WebContentsImpl *>(web_contents)->GetBrowserPluginGuest()->GetScreenCoordinates(gfx::Point()); + + // Adjust (x,y) position for offset from guest to embedder. + new_params.x += guest_coordinates.x(); + new_params.y += guest_coordinates.y(); + + return false; +} + +} // namespace extensions diff --git a/src/core/extensions/mime_handler_view_guest_delegate_qt.h b/src/core/extensions/mime_handler_view_guest_delegate_qt.h new file mode 100644 index 000000000..b679c7a38 --- /dev/null +++ b/src/core/extensions/mime_handler_view_guest_delegate_qt.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Portions copyright 2015 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MIME_HANDLER_VIEW_GUEST_DELEGATE_QT_H_ +#define MIME_HANDLER_VIEW_GUEST_DELEGATE_QT_H_ + +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h" +#include "content/browser/web_contents/web_contents_view.h" +#include "content/public/browser/web_contents.h" + +namespace content { +struct ContextMenuParams; +} + +namespace extensions { +class MimeHandlerViewGuest; + +class MimeHandlerViewGuestDelegateQt : public MimeHandlerViewGuestDelegate +{ +public: + explicit MimeHandlerViewGuestDelegateQt(MimeHandlerViewGuest *guest); + ~MimeHandlerViewGuestDelegateQt() override; + + bool HandleContextMenu(content::WebContents *web_contents, + const content::ContextMenuParams ¶ms) override; + +private: + MimeHandlerViewGuest *guest_; // Owns us. + + DISALLOW_COPY_AND_ASSIGN(MimeHandlerViewGuestDelegateQt); +}; + +} // namespace extensions + +#endif // MIME_HANDLER_VIEW_GUEST_DELEGATE_QT_H_ diff --git a/src/core/extensions/pdf_web_contents_helper_client_qt.h b/src/core/extensions/pdf_web_contents_helper_client_qt.h new file mode 100644 index 000000000..a22feb138 --- /dev/null +++ b/src/core/extensions/pdf_web_contents_helper_client_qt.h @@ -0,0 +1,29 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PDF_WEB_CONTENTS_HELPER_CLIENT_QT_H_ +#define PDF_WEB_CONTENTS_HELPER_CLIENT_QT_H_ + +#include "base/macros.h" +#include "components/pdf/browser/pdf_web_contents_helper_client.h" + +namespace extensions { + +class PDFWebContentsHelperClientQt : public pdf::PDFWebContentsHelperClient { +public: + PDFWebContentsHelperClientQt(); + ~PDFWebContentsHelperClientQt() override; + +private: + // pdf::PDFWebContentsHelperClient: + void UpdateContentRestrictions(content::WebContents* contents, int content_restrictions) override; + void OnPDFHasUnsupportedFeature(content::WebContents* contents) override; + void OnSaveURL(content::WebContents* contents) override; + + DISALLOW_COPY_AND_ASSIGN(PDFWebContentsHelperClientQt); +}; + +} // namespace extensions + +#endif // PDF_WEB_CONTENTS_HELPER_CLIENT_QT_H_ diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 3ded5ec41..a495ce6f2 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -41,6 +41,7 @@ #include "type_conversion.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/file_select_listener.h" #include <QFileInfo> #include <QDir> @@ -49,18 +50,20 @@ namespace QtWebEngineCore { -FilePickerController::FilePickerController(FileChooserMode mode, content::RenderFrameHost *frameHost, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject *parent) +FilePickerController::FilePickerController(FileChooserMode mode, std::unique_ptr<content::FileSelectListener> listener, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject *parent) : QObject(parent) , m_defaultFileName(defaultFileName) , m_acceptedMimeTypes(acceptedMimeTypes) - , m_frameHost(frameHost) + , m_listener(std::move(listener)) , m_mode(mode) { } +FilePickerController::~FilePickerController() = default; + void FilePickerController::accepted(const QStringList &files) { - FilePickerController::filesSelectedInChooser(files, m_frameHost); + FilePickerController::filesSelectedInChooser(files); } void FilePickerController::accepted(const QVariant &files) @@ -77,12 +80,12 @@ void FilePickerController::accepted(const QVariant &files) qWarning("An unhandled type '%s' was provided in FilePickerController::accepted(QVariant)", files.typeName()); } - FilePickerController::filesSelectedInChooser(stringList, m_frameHost); + FilePickerController::filesSelectedInChooser(stringList); } void FilePickerController::rejected() { - FilePickerController::filesSelectedInChooser(QStringList(), m_frameHost); + FilePickerController::filesSelectedInChooser(QStringList()); } static QStringList listRecursively(const QDir &dir) @@ -99,19 +102,29 @@ static QStringList listRecursively(const QDir &dir) return ret; } -ASSERT_ENUMS_MATCH(FilePickerController::Open, content::FileChooserParams::Open) -ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, content::FileChooserParams::OpenMultiple) -ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, content::FileChooserParams::UploadFolder) -ASSERT_ENUMS_MATCH(FilePickerController::Save, content::FileChooserParams::Save) +ASSERT_ENUMS_MATCH(FilePickerController::Open, blink::mojom::FileChooserParams_Mode::kOpen) +ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, blink::mojom::FileChooserParams_Mode::kOpenMultiple) +ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, blink::mojom::FileChooserParams_Mode::kUploadFolder) +ASSERT_ENUMS_MATCH(FilePickerController::Save, blink::mojom::FileChooserParams_Mode::kSave) -void FilePickerController::filesSelectedInChooser(const QStringList &filesList, content::RenderFrameHost *frameHost) +void FilePickerController::filesSelectedInChooser(const QStringList &filesList) { - Q_ASSERT(frameHost); QStringList files(filesList); if (this->m_mode == UploadFolder && !filesList.isEmpty() && QFileInfo(filesList.first()).isDir()) // Enumerate the directory files = listRecursively(QDir(filesList.first())); - frameHost->FilesSelectedInChooser(toVector<content::FileChooserFileInfo>(files), static_cast<content::FileChooserParams::Mode>(this->m_mode)); + + std::vector<blink::mojom::FileChooserFileInfoPtr> chooser_files; + for (const auto &file : qAsConst(files)) { + chooser_files.push_back(blink::mojom::FileChooserFileInfo::NewNativeFile( + blink::mojom::NativeFileInfo::New(toFilePath(file), base::string16()))); + } + + if (files.isEmpty()) + m_listener->FileSelectionCanceled(); + else + m_listener->FileSelected(std::move(chooser_files), + static_cast<blink::mojom::FileChooserParams::Mode>(this->m_mode)); } QStringList FilePickerController::acceptedMimeTypes() const diff --git a/src/core/file_picker_controller.h b/src/core/file_picker_controller.h index 7507cf358..613d3ad9b 100644 --- a/src/core/file_picker_controller.h +++ b/src/core/file_picker_controller.h @@ -52,11 +52,14 @@ #define FILE_PICKER_CONTROLLER_H #include "qtwebenginecoreglobal_p.h" + +#include <memory> + #include <QObject> #include <QStringList> namespace content { - class RenderFrameHost; + class FileSelectListener; } namespace QtWebEngineCore { @@ -71,7 +74,8 @@ public: Save }; - FilePickerController(FileChooserMode mode, content::RenderFrameHost *contents, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject * = 0); + FilePickerController(FileChooserMode mode, std::unique_ptr<content::FileSelectListener> listener, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject * = 0); + ~FilePickerController() override; QStringList acceptedMimeTypes() const; QString defaultFileName() const; FileChooserMode mode() const; @@ -82,10 +86,10 @@ public Q_SLOTS: void rejected(); private: - void filesSelectedInChooser(const QStringList &filesList, content::RenderFrameHost *contents); + void filesSelectedInChooser(const QStringList &filesList); QString m_defaultFileName; QStringList m_acceptedMimeTypes; - content::RenderFrameHost *m_frameHost; + std::unique_ptr<content::FileSelectListener> m_listener; FileChooserMode m_mode; }; diff --git a/src/core/gn_run.pro b/src/core/gn_run.pro index 9860c4541..0219a2be9 100644 --- a/src/core/gn_run.pro +++ b/src/core/gn_run.pro @@ -49,7 +49,7 @@ build_pass|!debug_and_release { ninjaflags = $$(NINJAFLAGS) isEmpty(ninjaflags):!silent: ninjaflags = "-v" - runninja.commands = $$NINJA $$ninjaflags -C $$gn_build_root QtWebEngineCore + runninja.commands = $$NINJA $$ninjaflags \$\(NINJAJOBS\) -C $$gn_build_root QtWebEngineCore QMAKE_EXTRA_TARGETS += runninja build_pass:build_all: default_target.target = all diff --git a/src/core/locked_ptr.h b/src/core/locked_ptr.h new file mode 100644 index 000000000..46d89819b --- /dev/null +++ b/src/core/locked_ptr.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCKED_PTR_H +#define LOCKED_PTR_H + +#include <base/bind_internal.h> + +#include <QtCore/qreadwritelock.h> + +namespace base { + +struct LockedPtrCore +{ + LockedPtrCore(uintptr_t data) : data(data) {} + + std::atomic<size_t> refCount{1}; + // Atomic so that WeakLockedPtr::get can still read it. + std::atomic<uintptr_t> data; + QReadWriteLock lock{QReadWriteLock::Recursive}; +}; + +enum class LockedPtrMode { Weak, Shared, Exclusive }; + +template<class T, LockedPtrMode mode> class LockedPtr; + +// A WeakLockedPtr<T> is something like shared_ptr<T*>. The T* value can only be +// accessed by atomic read. +template<class T> using WeakLockedPtr = LockedPtr<T, LockedPtrMode::Weak>; + +// A SharedLockedPtr<T> is like WeakLockedPtr<T>, but the T* value is prevented +// from changing for the lifetime of the SharedLockedPtr by holding a +// shared-exclusive mutex in shared mode. +template<class T> using SharedLockedPtr = LockedPtr<T, LockedPtrMode::Shared>; + +// An ExclusiveLockedPtr<T> is like SharedLockedPtr<T>, but the mutex is held in +// exclusive mode. Only in this mode can the T* value be changed. +template<class T> using ExclusiveLockedPtr = LockedPtr<T, LockedPtrMode::Exclusive>; + +template<class T, LockedPtrMode mode> +class LockedPtr +{ + template<class T1> + static constexpr bool canConstructFrom = + std::is_same<T, T1>::value || + std::is_same<T, const T1>::value; + +public: + constexpr LockedPtr() {} + constexpr LockedPtr(std::nullptr_t) {} + + LockedPtr(const LockedPtr &that) + { + m_core = that.m_core; + lock(); + } + + LockedPtr &operator=(const LockedPtr &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + LockedPtr(LockedPtr &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + LockedPtr &operator=(LockedPtr &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(const LockedPtr<T1, mode1> &that) + { + m_core = that.m_core; + lock(); + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(const LockedPtr<T1, mode1> &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(LockedPtr<T1, mode> &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(LockedPtr<T1, mode> &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + ~LockedPtr() + { + unlock(); + } + + T *get() const + { + if (m_core) { + if (mode == LockedPtrMode::Weak) + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_acquire)); + else + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_relaxed)); + } + return nullptr; + } + + void set(T *value) + { + static_assert(mode == LockedPtrMode::Exclusive, ""); + DCHECK(m_core); + m_core->data.store(reinterpret_cast<uintptr_t>(value), std::memory_order_release); + } + + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + explicit operator bool() const { return get(); } + + bool MaybeValid() const { return m_core; } + + static LockedPtr create(T *value) + { + return new LockedPtrCore(reinterpret_cast<uintptr_t>(value)); + } + +private: + template<class T1, LockedPtrMode mode1> friend class LockedPtr; + + LockedPtr(LockedPtrCore *core) + : m_core(core) + {} + + void lock() + { + if (m_core) { + ++m_core->refCount; + + if (mode == LockedPtrMode::Shared) + m_core->lock.lockForRead(); + else if (mode == LockedPtrMode::Exclusive) + m_core->lock.lockForWrite(); + } + } + + void unlock() + { + if (m_core) { + if (mode != LockedPtrMode::Weak) + m_core->lock.unlock(); + + if (--m_core->refCount == 0) + delete m_core; + } + } + + LockedPtrCore *m_core = nullptr; +}; + +// This makes Bind check the pointer before calling the functor. +template<class T> +struct IsWeakReceiver<WeakLockedPtr<T>> : std::true_type {}; + +// By converting the WeakLockedPtr into a SharedLockedPtr we prevent the +// pointed-to object from being destroyed during the base::Callback::Run call. +// +// Unwrap() is called before checking the pointer, so there's no race condition. +template<class T> +struct BindUnwrapTraits<WeakLockedPtr<T>> +{ + static SharedLockedPtr<T> Unwrap(const WeakLockedPtr<T> &o) + { + return o; + } +}; + +// Like base::WeakPtrFactory, but InvalidateWeakPtrs *waits* until all currently +// executing base::Callbacks are finished. Queued up base::Callbacks are still +// canceled, exactly like with WeakPtrFactory. +// +// Consider, for example, the function +// +// void fun() +// { +// MyClass *myClass = new MyClass; +// myClass->scheduleDoStuff(); +// delete myClass; // ??? +// } +// +// where +// +// class MyClass +// { +// public: +// void scheduleDoStuff() +// { +// content::BrowserThread::PostTask( +// content::BrowserThread::IO, FROM_HERE, +// base::BindOnce(&MyClass::doStuff, m_weakPtrFactory.GetWeakPtr())); +// } +// void doStuff(); +// private: +// //base::WeakPtrFactory m_weakPtrFactory{this}; +// base::LockedPtrFactory m_weakPtrFactory{this}; +// }; +// +// What happens if the 'delete myClass' line is executed concurrently with +// MyClass::doStuff? +// +// With WeakPtrs we get a segfault or perhaps memory corruption. +// +// With LockedPtrs we get no crash and no corruption: LockedPtrFactory's +// destructor will wait until doStuff is done before continuing. +template<class T> +class LockedPtrFactory +{ +public: + explicit LockedPtrFactory(T *value) + : m_ptr(WeakLockedPtr<T>::create(value)) + {} + + ~LockedPtrFactory() + { + InvalidateWeakPtrs(); + } + + WeakLockedPtr<T> GetWeakPtr() { return m_ptr; } + WeakLockedPtr<const T> GetWeakPtr() const { return m_ptr; } + SharedLockedPtr<T> GetSharedPtr() { return m_ptr; } + SharedLockedPtr<const T> GetSharedPtr() const { return m_ptr; } + ExclusiveLockedPtr<T> GetExclusivePtr() { return m_ptr; } + ExclusiveLockedPtr<const T> GetExclusivePtr() const { return m_ptr; } + + void InvalidateWeakPtrs() + { + if (ExclusiveLockedPtr<T> ptr = m_ptr) + ptr.set(nullptr); + } + +private: + WeakLockedPtr<T> m_ptr; +}; + +} // namespace base + +#endif // !LOCKED_PTR_H diff --git a/src/core/login_delegate_qt.cpp b/src/core/login_delegate_qt.cpp index 9659b354a..0050f87c7 100644 --- a/src/core/login_delegate_qt.cpp +++ b/src/core/login_delegate_qt.cpp @@ -43,17 +43,32 @@ #include "login_delegate_qt.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/resource_request_info.h" +#include "content/public/browser/stream_info.h" +#include "extensions/buildflags/buildflags.h" +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/browser/info_map.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + #include "net/url_request/url_request.h" #include "authentication_dialog_controller.h" #include "authentication_dialog_controller_p.h" +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/extension_system_qt.h" +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +#include "resource_context_qt.h" #include "type_conversion.h" #include "web_contents_view_qt.h" +#include "web_engine_context.h" namespace QtWebEngineCore { @@ -66,14 +81,9 @@ LoginDelegateQt::LoginDelegateQt( : m_authInfo(authInfo) , m_url(url) , m_auth_required_callback(std::move(auth_required_callback)) + , m_webContentsGetter(web_contents_getter) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::Bind(&LoginDelegateQt::triggerDialog, - this, - web_contents_getter)); } LoginDelegateQt::~LoginDelegateQt() @@ -81,6 +91,13 @@ LoginDelegateQt::~LoginDelegateQt() Q_ASSERT(m_dialogController.isNull()); } +void LoginDelegateQt::triggerDialog() +{ + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&LoginDelegateQt::triggerDialogOnUI, this)); +} + void LoginDelegateQt::OnRequestCancelled() { destroy(); @@ -102,16 +119,34 @@ QString LoginDelegateQt::host() const return QString::fromStdString(m_authInfo->challenger.host()); } +int LoginDelegateQt::port() const +{ + return m_authInfo->challenger.port(); +} + bool LoginDelegateQt::isProxy() const { return m_authInfo->is_proxy; } -void LoginDelegateQt::triggerDialog(const content::ResourceRequestInfo::WebContentsGetter &webContentsGetter) +void LoginDelegateQt::triggerDialogOnUI() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + if (isProxy()) { + // workaround for 'ws' redefined symbols when including QNetworkProxy + auto authentication = WebEngineContext::qProxyNetworkAuthentication(host(), port()); + if (std::get<0>(authentication)) { + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::IO }, + base::BindOnce(&LoginDelegateQt::sendAuthToRequester, this, true, + std::get<1>(authentication), std::get<2>(authentication))); + + return; + } + } content::WebContentsImpl *webContents = - static_cast<content::WebContentsImpl *>(webContentsGetter.Run()); + static_cast<content::WebContentsImpl *>(m_webContentsGetter.Run()); if (!webContents) return; WebContentsAdapterClient *client = WebContentsViewQt::from(webContents->GetView())->client(); diff --git a/src/core/login_delegate_qt.h b/src/core/login_delegate_qt.h index 9ce5df843..3a9c073cd 100644 --- a/src/core/login_delegate_qt.h +++ b/src/core/login_delegate_qt.h @@ -66,24 +66,28 @@ public: ~LoginDelegateQt(); + void triggerDialog(); + // LoginDelegate implementation void OnRequestCancelled() override; QUrl url() const; QString realm() const; QString host() const; + int port() const; bool isProxy() const; void sendAuthToRequester(bool success, const QString &user, const QString &password); private: - void triggerDialog(const content::ResourceRequestInfo::WebContentsGetter &); + void triggerDialogOnUI(); void destroy(); scoped_refptr<net::AuthChallengeInfo> m_authInfo; GURL m_url; LoginAuthRequiredCallback m_auth_required_callback; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; // This member is used to keep authentication dialog controller alive until // authorization is sent or cancelled. diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp index 2bac62084..a0b19cad3 100644 --- a/src/core/media_capture_devices_dispatcher.cpp +++ b/src/core/media_capture_devices_dispatcher.cpp @@ -49,10 +49,10 @@ #include "web_engine_settings.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/media/webrtc/desktop_streams_registry.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" +#include "content/public/browser/desktop_streams_registry.h" #include "content/public/browser/media_capture_devices.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" @@ -95,16 +95,16 @@ void getDevicesForDesktopCapture(content::MediaStreamDevices *devices, content:: DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add selected desktop source to the list. - devices->push_back(content::MediaStreamDevice(content::MEDIA_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen")); + devices->push_back(content::MediaStreamDevice(content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen")); if (captureAudio) { if (mediaId.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { devices->push_back( - content::MediaStreamDevice(content::MEDIA_DESKTOP_AUDIO_CAPTURE, + content::MediaStreamDevice(content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, mediaId.ToString(), "Tab audio")); } else { // Use the special loopback device ID for system audio capture. devices->push_back(content::MediaStreamDevice( - content::MEDIA_DESKTOP_AUDIO_CAPTURE, + content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, media::AudioDeviceDescription::kLoopbackInputDeviceId, "System Audio")); } @@ -157,12 +157,12 @@ WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const co if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaAudioCapture; - else if (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) + else if (request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaDesktopAudioCapture; if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaVideoCapture; - else if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) + else if (request.video_type == content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaDesktopVideoCapture; return requestFlags; @@ -233,8 +233,9 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: // Post a task to process next queued request. It has to be done // asynchronously to make sure that calling infobar is not destroyed until // after this function returns. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, base::Unretained(this), webContents)); + base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, + base::Unretained(this), webContents)); } std::move(callback).Run(devices, devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : content::MEDIA_DEVICE_OK, @@ -275,12 +276,12 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(WebContentsAdapter DCHECK_CURRENTLY_ON(BrowserThread::UI); // Let's not support tab capture for now. - if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { + if (request.video_type == content::MEDIA_GUM_TAB_VIDEO_CAPTURE || request.audio_type == content::MEDIA_GUM_TAB_AUDIO_CAPTURE) { std::move(callback).Run(content::MediaStreamDevices(), content::MEDIA_DEVICE_NOT_SUPPORTED, std::unique_ptr<content::MediaStreamUI>()); return; } - if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { + if (request.video_type == content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) { const bool screenCaptureEnabled = adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::ScreenCaptureEnabled); const bool originIsSecure = content::IsOriginSecure(request.security_origin); @@ -305,7 +306,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: { content::MediaStreamDevices devices; - if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE || request.requested_video_device_id.empty()) { + if (request.video_type != content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.requested_video_device_id.empty()) { std::move(callback).Run(devices, content::MEDIA_DEVICE_INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); return; } @@ -319,10 +320,10 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: // The extension name that the stream is registered with. std::string originalExtensionName; // Resolve DesktopMediaID for the specified device id. - mediaId = getDesktopStreamsRegistry()->RequestMediaForStreamId( + mediaId = content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId( request.requested_video_device_id, main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(), request.security_origin, - &originalExtensionName); + &originalExtensionName, content::kRegistryStreamTypeDesktop); } // Received invalid device id. @@ -332,7 +333,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: } // Audio is only supported for screen capture streams. - bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE); + bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE); getDevicesForDesktopCapture(&devices, mediaId, capture_audio); @@ -390,22 +391,13 @@ void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDe } } -DesktopStreamsRegistry *MediaCaptureDevicesDispatcher::getDesktopStreamsRegistry() -{ - if (!m_desktopStreamsRegistry) - m_desktopStreamsRegistry.reset(new DesktopStreamsRegistry()); - return m_desktopStreamsRegistry.get(); -} - void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(int render_process_id, int render_frame_id, int page_request_id, const GURL &security_origin, content::MediaStreamType stream_type, content::MediaRequestState state) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, - base::Unretained(this), render_process_id, render_frame_id, - page_request_id, security_origin, stream_type, state)); + base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, + base::Unretained(this), render_process_id, render_frame_id, + page_request_id, security_origin, stream_type, state)); } void MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread(int render_process_id, diff --git a/src/core/media_capture_devices_dispatcher.h b/src/core/media_capture_devices_dispatcher.h index 0e5aa38be..cc6e60ede 100644 --- a/src/core/media_capture_devices_dispatcher.h +++ b/src/core/media_capture_devices_dispatcher.h @@ -57,8 +57,6 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/media_stream_request.h" -class DesktopStreamsRegistry; - namespace QtWebEngineCore { // This singleton is used to receive updates about media events from the content @@ -94,8 +92,6 @@ private: content::MediaStreamType /*stream_type*/, bool /*is_secure*/) override {} - DesktopStreamsRegistry *getDesktopStreamsRegistry(); - friend struct base::DefaultSingletonTraits<MediaCaptureDevicesDispatcher>; typedef base::RepeatingCallback<void(const content::MediaStreamDevices &devices, @@ -129,8 +125,6 @@ private: RequestsQueues m_pendingRequests; - std::unique_ptr<DesktopStreamsRegistry> m_desktopStreamsRegistry; - content::NotificationRegistrar m_notificationsRegistrar; DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher); diff --git a/src/core/net/client_cert_override.cpp b/src/core/net/client_cert_override.cpp new file mode 100644 index 000000000..305f0cef0 --- /dev/null +++ b/src/core/net/client_cert_override.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "client_cert_override.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/task/post_task.h" +#include "base/callback_forward.h" +#include "content/public/browser/browser_task_traits.h" +#include "net/ssl/client_cert_store.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_private_key.h" +#include "net/cert/x509_certificate.h" +#include "third_party/boringssl/src/include/openssl/pem.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" + +#include "client_cert_store_data.h" +#include "profile_io_data_qt.h" + +#include <QtNetwork/qtnetworkglobal.h> + +#if defined(USE_NSS_CERTS) +#include "net/ssl/client_cert_store_nss.h" +#endif + +#if defined(OS_WIN) +#include "net/ssl/client_cert_store_win.h" +#endif + +#if defined(OS_MACOSX) +#include "net/ssl/client_cert_store_mac.h" +#endif + +namespace { + +class ClientCertIdentityOverride : public net::ClientCertIdentity +{ +public: + ClientCertIdentityOverride(scoped_refptr<net::X509Certificate> cert, scoped_refptr<net::SSLPrivateKey> key) + : net::ClientCertIdentity(std::move(cert)), m_key(std::move(key)) {} + ~ClientCertIdentityOverride() override = default; + + void AcquirePrivateKey(const base::Callback<void(scoped_refptr<net::SSLPrivateKey>)> &private_key_callback) override + { + private_key_callback.Run(m_key); + } + +#if defined(OS_MACOSX) + SecIdentityRef sec_identity_ref() const override + { + return nullptr; + } +#endif + +private: + scoped_refptr<net::SSLPrivateKey> m_key; +}; + +} // namespace + +namespace QtWebEngineCore { + +ClientCertOverrideStore::ClientCertOverrideStore(ClientCertificateStoreData *storeData) + : ClientCertStore() + , m_storeData(storeData) + , m_nativeStore(createNativeStore()) +{ +} + +ClientCertOverrideStore::~ClientCertOverrideStore() = default; + +#if QT_CONFIG(ssl) +net::ClientCertIdentityList ClientCertOverrideStore::GetClientCertsOnUIThread(const net::SSLCertRequestInfo &cert_request_info) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + const auto &clientCertOverrideData = m_storeData->extraCerts; + // Look for certificates in memory store + for (int i = 0; i < clientCertOverrideData.length(); i++) { + scoped_refptr<net::X509Certificate> cert = clientCertOverrideData[i]->certPtr; + if (cert != NULL && cert->IsIssuedByEncoded(cert_request_info.cert_authorities)) { + net::ClientCertIdentityList selected_identities; + selected_identities.push_back(std::make_unique<ClientCertIdentityOverride>(cert, clientCertOverrideData[i]->keyPtr)); + return selected_identities; + } + } + return net::ClientCertIdentityList(); +} + +void ClientCertOverrideStore::GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback, + net::ClientCertIdentityList &&result) +{ + // Continue with native cert store if matching certificatse were not found in memory + if (result.empty() && m_nativeStore) + m_nativeStore->GetClientCerts(cert_request_info, callback); + else + callback.Run(std::move(result)); +} + +#endif // QT_CONFIG(ssl) + +void ClientCertOverrideStore::GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback) +{ +#if QT_CONFIG(ssl) + // Access the user-provided data from the UI thread, but return on whatever thread this is. + if (base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&ClientCertOverrideStore::GetClientCertsOnUIThread, + base::Unretained(this), base::ConstRef(cert_request_info)), + base::BindOnce(&ClientCertOverrideStore::GetClientCertsReturn, + base::Unretained(this), base::ConstRef(cert_request_info), callback)) + ) { + return; + } +#endif // QT_CONFIG(ssl) + + // Continue with native cert store if we failed to post task + if (m_nativeStore) + m_nativeStore->GetClientCerts(cert_request_info, callback); + else + callback.Run(net::ClientCertIdentityList()); +} + +// static +std::unique_ptr<net::ClientCertStore> ClientCertOverrideStore::createNativeStore() +{ +#if defined(USE_NSS_CERTS) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); +#elif defined(OS_WIN) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); +#elif defined(OS_MACOSX) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); +#else + return nullptr; +#endif +} +} // namespace QtWebEngineCore diff --git a/src/core/net/client_cert_override.h b/src/core/net/client_cert_override.h new file mode 100644 index 000000000..35c1f96af --- /dev/null +++ b/src/core/net/client_cert_override.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLIENT_CERT_OVERRIDE_P_H +#define CLIENT_CERT_OVERRIDE_P_H + +#include "net/ssl/client_cert_store.h" +#include "base/callback_forward.h" +#include "net/cert/x509_certificate.h" + +namespace net { +class SSLCertRequestInfo; +} // namespace net + +namespace QtWebEngineCore { +struct ClientCertificateStoreData; + +class ClientCertOverrideStore : public net::ClientCertStore +{ +public: + ClientCertOverrideStore(ClientCertificateStoreData *storeData); + virtual ~ClientCertOverrideStore() override; + void GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback) override; +private: + static std::unique_ptr<net::ClientCertStore> createNativeStore(); + net::ClientCertIdentityList GetClientCertsOnUIThread(const net::SSLCertRequestInfo &request); + void GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback, + net::ClientCertIdentityList &&result); + ClientCertificateStoreData *m_storeData; + std::unique_ptr<net::ClientCertStore> m_nativeStore; +}; + +} // QtWebEngineCore + +#endif + + diff --git a/src/core/net/client_cert_store_data.cpp b/src/core/net/client_cert_store_data.cpp new file mode 100644 index 000000000..d1018a063 --- /dev/null +++ b/src/core/net/client_cert_store_data.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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 "net/client_cert_store_data.h" + +#if QT_CONFIG(ssl) +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "net/base/net_errors.h" +#include "net/cert/x509_certificate.h" +#include "net/ssl/ssl_platform_key_util.h" +#include "net/ssl/ssl_private_key.h" +#include "net/ssl/threaded_ssl_private_key.h" + +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "third_party/boringssl/src/include/openssl/digest.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" +#include "third_party/boringssl/src/include/openssl/pem.h" + +#include "QtCore/qbytearray.h" + +namespace { + +class SSLPlatformKeyOverride : public net::ThreadedSSLPrivateKey::Delegate { +public: + SSLPlatformKeyOverride(const QByteArray &sslKeyInBytes) + { + m_mem = BIO_new_mem_buf(sslKeyInBytes, -1); + m_key = PEM_read_bio_PrivateKey(m_mem, nullptr, nullptr, nullptr); + } + + ~SSLPlatformKeyOverride() override + { + if (m_key) + EVP_PKEY_free(m_key); + if (m_mem) + BIO_free(m_mem); + } + + net::Error Sign(uint16_t algorithm, base::span<const uint8_t> input, std::vector<uint8_t> *signature) override + { + bssl::ScopedEVP_MD_CTX ctx; + EVP_PKEY_CTX *pctx; + if (!EVP_DigestSignInit(ctx.get(), &pctx, + SSL_get_signature_algorithm_digest(algorithm), + nullptr, m_key)) { + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + } + + if (SSL_is_signature_algorithm_rsa_pss(algorithm)) { + if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || + !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* hash length */)) { + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + } + } + size_t sig_len = 0; + if (!EVP_DigestSign(ctx.get(), NULL, &sig_len, input.data(), input.size())) + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + signature->resize(sig_len); + if (!EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(), input.size())) + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + signature->resize(sig_len); + return net::OK; + } + + std::vector<uint16_t> GetAlgorithmPreferences() override + { + return { SSL_SIGN_RSA_PKCS1_SHA1, SSL_SIGN_RSA_PKCS1_SHA512 + , SSL_SIGN_RSA_PKCS1_SHA384, SSL_SIGN_RSA_PKCS1_SHA256 }; + } + +private: + EVP_PKEY *m_key; + BIO *m_mem; + + DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyOverride); +}; + +scoped_refptr<net::SSLPrivateKey> wrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes) +{ + if (sslKeyInBytes.isEmpty()) + return nullptr; + + return base::MakeRefCounted<net::ThreadedSSLPrivateKey>( + std::make_unique<SSLPlatformKeyOverride>(sslKeyInBytes), + net::GetSSLPlatformKeyTaskRunner()); +} + +} // namespace + +namespace QtWebEngineCore { + +void ClientCertificateStoreData::add(const QSslCertificate &certificate, const QSslKey &privateKey) +{ + QByteArray sslKeyInBytes = privateKey.toPem(); + QByteArray certInBytes = certificate.toDer(); + + Entry *data = new Entry; + data->keyPtr = wrapOpenSSLPrivateKey(sslKeyInBytes); + data->certPtr = net::X509Certificate::CreateFromBytes(certInBytes.data(), certInBytes.length()); + data->key = privateKey; + data->certificate = certificate; + extraCerts.append(data); +} + +void ClientCertificateStoreData::remove(const QSslCertificate &certificate) +{ + auto it = extraCerts.begin(); + while (it != extraCerts.end()) { + const QtWebEngineCore::ClientCertificateStoreData::Entry *overrideData = *it; + if (certificate.toDer() == overrideData->certificate.toDer()) { + it = extraCerts.erase(it); + delete overrideData; + continue; + } + ++it; + } +} + +void ClientCertificateStoreData::clear() +{ + qDeleteAll(extraCerts); + extraCerts.clear(); +} + +} // namespace QtWebEngineCore + +#endif diff --git a/src/core/net/client_cert_store_data.h b/src/core/net/client_cert_store_data.h new file mode 100644 index 000000000..7f83f4b60 --- /dev/null +++ b/src/core/net/client_cert_store_data.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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 CLIENT_CERT_STORE_DATA_H +#define CLIENT_CERT_STORE_DATA_H + +#include "qtwebenginecoreglobal.h" +#include "qtnetworkglobal.h" + +#if QT_CONFIG(ssl) +#include "base/memory/ref_counted.h" + +#include <QtCore/qvector.h> +#include <QtNetwork/qsslcertificate.h> +#include <QtNetwork/qsslkey.h> + +namespace net { +class SSLPrivateKey; +class X509Certificate; +} + +namespace QtWebEngineCore { + +struct ClientCertificateStoreData { + struct Entry { + QSslKey key; + QSslCertificate certificate; + scoped_refptr<net::X509Certificate> certPtr; + scoped_refptr<net::SSLPrivateKey> keyPtr; + }; + + void add(const QSslCertificate &certificate, const QSslKey &privateKey); + void remove(const QSslCertificate &certificate); + void clear(); + + QVector<Entry*> extraCerts; +}; + +} // namespace QtWebEngineCore + +#endif +#endif // CLIENT_CERT_STORE_DATA_H diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp index bb89e9e5f..3253b08b9 100644 --- a/src/core/net/cookie_monster_delegate_qt.cpp +++ b/src/core/net/cookie_monster_delegate_qt.cpp @@ -41,6 +41,8 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/cookies/cookie_util.h" @@ -87,8 +89,8 @@ void CookieMonsterDelegateQt::getAllCookies(quint64 callbackId) net::CookieMonster::GetCookieListCallback callback = base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback) @@ -108,9 +110,9 @@ void CookieMonsterDelegateQt::setCookie(quint64 callbackId, const QNetworkCookie GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this, - gurl, cookie.toRawForm().toStdString(), std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this, + gurl, cookie.toRawForm().toStdString(), std::move(callback))); } void CookieMonsterDelegateQt::SetCookieOnIOThread( @@ -131,9 +133,9 @@ void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const Q GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this, - gurl, cookie.name().toStdString())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this, + gurl, cookie.name().toStdString())); } void CookieMonsterDelegateQt::DeleteCookieOnIOThread(const GURL& url, const std::string& cookie_name) @@ -149,8 +151,8 @@ void CookieMonsterDelegateQt::deleteSessionCookies(quint64 callbackId) net::CookieMonster::DeleteCallback callback = base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback) @@ -166,8 +168,8 @@ void CookieMonsterDelegateQt::deleteAllCookies(quint64 callbackId) net::CookieMonster::DeleteCallback callback = base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback) @@ -238,26 +240,23 @@ void CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread(qint64 callbackId, for (auto &&cookie : cookies) rawCookies += toQt(cookie).toRawForm() % QByteArrayLiteral("\n"); - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies)); } void CookieMonsterDelegateQt::SetCookieCallbackOnIOThread(qint64 callbackId, bool success) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, success)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, success)); } void CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies)); } void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies) diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 37309931e..3641cb845 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -39,22 +39,26 @@ #include "network_delegate_qt.h" -#include "profile_adapter.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_request_info.h" -#include "cookie_monster_delegate_qt.h" -#include "ui/base/page_transition_types.h" -#include "profile_io_data_qt.h" #include "net/base/load_flags.h" #include "net/url_request/url_request.h" +#include "ui/base/page_transition_types.h" + +#include "profile_adapter.h" +#include "cookie_monster_delegate_qt.h" +#include "profile_io_data_qt.h" #include "qwebengineurlrequestinfo.h" #include "qwebengineurlrequestinfo_p.h" #include "qwebengineurlrequestinterceptor.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_contents_view_qt.h" +#include "url_request_notification.h" namespace QtWebEngineCore { @@ -81,128 +85,18 @@ WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::Page } } -namespace { - -QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) +static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) { if (resourceType >= 0 && resourceType < content::ResourceType(QWebEngineUrlRequestInfo::ResourceTypeLast)) return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType); return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } -QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) +static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) { return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); } -// Notifies WebContentsAdapterClient of a new URLRequest. -class URLRequestNotification { -public: - URLRequestNotification(net::URLRequest *request, - const QUrl &url, - bool isMainFrameRequest, - int navigationType, - int frameTreeNodeId, - net::CompletionOnceCallback callback) - : m_request(request) - , m_url(url) - , m_isMainFrameRequest(isMainFrameRequest) - , m_navigationType(navigationType) - , m_frameTreeNodeId(frameTreeNodeId) - , m_callback(std::move(callback)) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); - - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::Bind(&URLRequestNotification::notify, base::Unretained(this))); - } - -private: - // Calls cancel() when the URLRequest is destroyed. - class UserData : public base::SupportsUserData::Data { - public: - UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} - ~UserData() { m_ptr->cancel(); } - static const char key[]; - private: - URLRequestNotification *m_ptr; - }; - - void cancel() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - // May run concurrently with notify() but we only touch m_request here. - - m_request = nullptr; - } - - void notify() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - // May run concurrently with cancel() so no peeking at m_request here. - - int error = net::OK; - content::WebContents *webContents = content::WebContents::FromFrameTreeNodeId(m_frameTreeNodeId); - if (webContents) { - int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; - WebContentsAdapterClient *client = - WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); - client->navigationRequested(m_navigationType, - m_url, - navigationRequestAction, - m_isMainFrameRequest); - error = net::ERR_FAILED; - switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { - case WebContentsAdapterClient::AcceptRequest: - error = net::OK; - break; - case WebContentsAdapterClient::IgnoreRequest: - error = net::ERR_ABORTED; - break; - } - DCHECK(error != net::ERR_FAILED); - } - - // Run the callback on the IO thread. - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), error)); - } - - void complete(int error) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - if (m_request) { - if (m_request->status().status() != net::URLRequestStatus::CANCELED) - std::move(m_callback).Run(error); - m_request->RemoveUserData(UserData::key); - } - - delete this; - } - - ~URLRequestNotification() {} - - net::URLRequest *m_request; - QUrl m_url; - bool m_isMainFrameRequest; - int m_navigationType; - int m_frameTreeNodeId; - net::CompletionOnceCallback m_callback; -}; - -const char URLRequestNotification::UserData::key[] = "QtWebEngineCore::URLRequestNotification"; - -} // namespace - NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { @@ -212,7 +106,6 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); Q_ASSERT(m_profileIOData); - const content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request); content::ResourceType resourceType = content::RESOURCE_TYPE_LAST_TYPE; @@ -231,49 +124,59 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet else firstPartyUrl = toQt(request->site_for_cookies()); - QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor(); - if (interceptor) { - QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), - toQt(navigationType), - qUrl, - firstPartyUrl, - QByteArray::fromStdString(request->method())); - QWebEngineUrlRequestInfo requestInfo(infoPrivate); - interceptor->interceptRequest(requestInfo); - m_profileIOData->releaseInterceptor(); - if (requestInfo.changed()) { - int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; - - if (qUrl != infoPrivate->url) - *newUrl = toGurl(infoPrivate->url); - - if (!infoPrivate->extraHeaders.isEmpty()) { - auto end = infoPrivate->extraHeaders.constEnd(); - for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) - request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), + toQt(navigationType), + qUrl, + firstPartyUrl, + QByteArray::fromStdString(request->method())); + QWebEngineUrlRequestInfo requestInfo(infoPrivate); + + // Deprecated =begin + // quick peek if deprecated + QWebEngineUrlRequestInterceptor* profileInterceptor = m_profileIOData->requestInterceptor(); + if (profileInterceptor && profileInterceptor->property("deprecated").toBool()) { + profileInterceptor = nullptr; + if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { + interceptor->interceptRequest(requestInfo); + m_profileIOData->releaseInterceptor(); + if (requestInfo.changed()) { + int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + + if (qUrl != infoPrivate->url) + *newUrl = toGurl(infoPrivate->url); + + if (!infoPrivate->extraHeaders.isEmpty()) { + auto end = infoPrivate->extraHeaders.constEnd(); + for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) + request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } + + if (result != net::OK) + return result; + + requestInfo.resetChanged(); } - - if (result != net::OK) - return result; + } else { + m_profileIOData->releaseInterceptor(); } - } else - m_profileIOData->releaseInterceptor(); + } + // Deprecated =cut if (!resourceInfo) return net::OK; - int frameTreeNodeId = resourceInfo->GetFrameTreeNodeId(); - // Only intercept MAIN_FRAME and SUB_FRAME with an associated render frame. - if (!content::IsResourceTypeFrame(resourceType) || frameTreeNodeId == -1) + if (!m_profileIOData->hasPageInterceptors() && !profileInterceptor && !content::IsResourceTypeFrame(resourceType)) return net::OK; + auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); new URLRequestNotification( request, - qUrl, resourceInfo->IsMainFrame(), - navigationType, - frameTreeNodeId, - std::move(callback) + newUrl, + std::move(requestInfo), + webContentsGetter, + std::move(callback), + profileInterceptor ? m_profileIOData->profileAdapter() : nullptr ); // We'll run the callback after we notified the UI thread. @@ -290,13 +193,18 @@ void NetworkDelegateQt::OnCompleted(net::URLRequest */*request*/, bool /*started bool NetworkDelegateQt::OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie & /*cookie*/, - net::CookieOptions*) + net::CookieOptions*, + bool allowedFromCaller) { + if (!allowedFromCaller) + return false; return canSetCookies(request.site_for_cookies(), request.url(), std::string()); } -bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest& request, const net::CookieList&) +bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest& request, const net::CookieList&, bool allowedFromCaller) { + if (!allowedFromCaller) + return false; return canGetCookies(request.site_for_cookies(), request.url()); } diff --git a/src/core/net/network_delegate_qt.h b/src/core/net/network_delegate_qt.h index e4ff196aa..842af5006 100644 --- a/src/core/net/network_delegate_qt.h +++ b/src/core/net/network_delegate_qt.h @@ -62,7 +62,7 @@ public: // net::NetworkDelegate implementation int OnBeforeURLRequest(net::URLRequest* request, net::CompletionOnceCallback callback, GURL* new_url) override; void OnURLRequestDestroyed(net::URLRequest* request) override; - bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, net::CookieOptions* options) override; + bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, net::CookieOptions* options, bool) override; int OnBeforeStartTransaction(net::URLRequest *request, const net::CompletionOnceCallback callback, net::HttpRequestHeaders *headers) override; void OnBeforeSendHeaders(net::URLRequest* request, const net::ProxyInfo& proxy_info, const net::ProxyRetryInfoMap& proxy_retry_info, net::HttpRequestHeaders* headers) override; @@ -75,7 +75,7 @@ public: void OnCompleted(net::URLRequest *request, bool started, int net_error) override; void OnPACScriptError(int, const base::string16&) override; net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(net::URLRequest*, const net::AuthChallengeInfo&, AuthCallback, net::AuthCredentials*) override; - bool OnCanGetCookies(const net::URLRequest&, const net::CookieList&) override; + bool OnCanGetCookies(const net::URLRequest&, const net::CookieList&, bool) override; bool OnCanAccessFile(const net::URLRequest&, const base::FilePath&, const base::FilePath&) const override; bool OnCanEnablePrivacyMode(const GURL&, const GURL&) const override; bool OnAreExperimentalCookieFeaturesEnabled() const override; diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp index 13b969281..ff8ab20aa 100644 --- a/src/core/net/proxy_config_service_qt.cpp +++ b/src/core/net/proxy_config_service_qt.cpp @@ -47,6 +47,7 @@ #include "base/bind.h" #include "content/public/browser/browser_thread.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" using content::BrowserThread; @@ -68,10 +69,13 @@ net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qt } } -ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService) +ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, ProxyPrefs::ConfigState initialState) : m_baseService(baseService.release()), m_usesSystemConfiguration(false), - m_registeredObserver(false) + m_registeredObserver(false), + m_prefConfig(initialConfig), + m_perfState(initialState) { } @@ -100,8 +104,10 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy ConfigAvailability systemAvailability = net::ProxyConfigService::CONFIG_UNSET; if (m_baseService.get()) systemAvailability = m_baseService->GetLatestProxyConfig(&systemConfig); - *config = systemConfig; - // make sure to get updates via OnProxyConfigChanged + ProxyPrefs::ConfigState configState; + systemAvailability = PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( + m_perfState, m_prefConfig, systemAvailability, systemConfig, + false, &configState, config); RegisterObserver(); return systemAvailability; } diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h index dcd303894..09e88d445 100644 --- a/src/core/net/proxy_config_service_qt.h +++ b/src/core/net/proxy_config_service_qt.h @@ -43,8 +43,10 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" +#include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" +#include "components/proxy_config/proxy_prefs.h" #include <QNetworkProxy> @@ -55,7 +57,9 @@ public: static net::ProxyServer fromQNetworkProxy(const QNetworkProxy &); - explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService); + explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, + ProxyPrefs::ConfigState initialState); ~ProxyConfigServiceQt() override; // ProxyConfigService implementation: @@ -76,7 +80,7 @@ private: void RegisterObserver(); std::unique_ptr<net::ProxyConfigService> m_baseService; - base::ObserverList<net::ProxyConfigService::Observer, true> m_observers; + base::ObserverList<net::ProxyConfigService::Observer, true>::Unchecked m_observers; // Keep the last state around. bool m_usesSystemConfiguration; @@ -86,6 +90,10 @@ private: // Indicates whether the base service registration is done. bool m_registeredObserver; + // Configuration as defined by prefs. + net::ProxyConfigWithAnnotation m_prefConfig; + ProxyPrefs::ConfigState m_perfState; + DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceQt); }; diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp new file mode 100644 index 000000000..74a77a7ec --- /dev/null +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrc_url_scheme_handler.h" + +#include <QtWebEngineCore/qwebengineurlrequestjob.h> + +#include <QFile> +#include <QFileInfo> +#include <QMimeDatabase> +#include <QMimeType> + +namespace QtWebEngineCore { + +void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) +{ + QByteArray requestMethod = job->requestMethod(); + if (requestMethod != "GET") { + job->fail(QWebEngineUrlRequestJob::RequestDenied); + return; + } + + QUrl requestUrl = job->requestUrl(); + QString requestPath = requestUrl.path(); + QScopedPointer<QFile> file(new QFile(':' + requestPath, job)); + QFileInfo fileInfo(*file); + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); + job->reply(mimeType.name().toUtf8(), file.take()); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/qrc_url_scheme_handler.h b/src/core/net/qrc_url_scheme_handler.h new file mode 100644 index 000000000..f6ca92879 --- /dev/null +++ b/src/core/net/qrc_url_scheme_handler.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRC_URL_SCHEME_HANDLER_H +#define QRC_URL_SCHEME_HANDLER_H + +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> + +namespace QtWebEngineCore { + +class QrcUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { +public: + void requestStarted(QWebEngineUrlRequestJob *) override; +}; + +} // namespace QtWebEngineCore + +#endif // !QRC_URL_SCHEME_HANDLER_H diff --git a/src/core/net/url_request_context_getter_qt.cpp b/src/core/net/url_request_context_getter_qt.cpp index 636d27358..6081a5e9f 100644 --- a/src/core/net/url_request_context_getter_qt.cpp +++ b/src/core/net/url_request_context_getter_qt.cpp @@ -40,6 +40,10 @@ #include "url_request_context_getter_qt.h" #include "profile_io_data_qt.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" + namespace QtWebEngineCore { URLRequestContextGetterQt::URLRequestContextGetterQt(ProfileIODataQt *data) @@ -59,7 +63,7 @@ net::URLRequestContext *URLRequestContextGetterQt::GetURLRequestContext() scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetterQt::GetNetworkTaskRunner() const { - return content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO); + return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}); } } // namespace QtWebEngineCore diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp index d371c7bff..cba9b4dc5 100644 --- a/src/core/net/url_request_custom_job.cpp +++ b/src/core/net/url_request_custom_job.cpp @@ -39,6 +39,9 @@ #include "url_request_custom_job.h" #include "url_request_custom_job_proxy.h" + +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" @@ -68,17 +71,30 @@ URLRequestCustomJob::~URLRequestCustomJob() if (m_device && m_device->isOpen()) m_device->close(); m_device = nullptr; - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::release, - m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); } void URLRequestCustomJob::Start() { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::initialize, - m_proxy, request()->url(), request()->method(), request()->initiator())); + HttpRequestHeaders requestHeaders = request()->extra_request_headers(); + std::map<std::string, std::string> headers; + net::HttpRequestHeaders::Iterator it(requestHeaders); + while (it.GetNext()) + headers.emplace(it.name(), it.value()); + if (!request()->referrer().empty()) + headers.emplace("Referer", request()->referrer()); + + // TODO: handle UploadDataStream, for instance using a QIODevice wrapper. + + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::initialize, + m_proxy, + request()->url(), + request()->method(), + request()->initiator(), + std::move(headers))); } void URLRequestCustomJob::Kill() @@ -94,9 +110,9 @@ void URLRequestCustomJob::Kill() m_pendingReadPos = 0; } m_device = nullptr; - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::release, - m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::release, + m_proxy)); URLRequestJob::Kill(); } diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp index 338bd7137..b5a7a55a7 100644 --- a/src/core/net/url_request_custom_job_delegate.cpp +++ b/src/core/net/url_request_custom_job_delegate.cpp @@ -40,9 +40,12 @@ #include "url_request_custom_job_delegate.h" #include "url_request_custom_job_proxy.h" -#include "type_conversion.h" -#include "net/base/net_errors.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "net/base/net_errors.h" + +#include "type_conversion.h" #include <QByteArray> @@ -51,11 +54,13 @@ namespace QtWebEngineCore { URLRequestCustomJobDelegate::URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, const QByteArray &method, - const QUrl &initiatorOrigin) + const QUrl &initiatorOrigin, + const QMap<QByteArray, QByteArray> &headers) : m_proxy(proxy), m_request(url), m_method(method), - m_initiatorOrigin(initiatorOrigin) + m_initiatorOrigin(initiatorOrigin), + m_requestHeaders(headers) { } @@ -78,32 +83,36 @@ QUrl URLRequestCustomJobDelegate::initiator() const return m_initiatorOrigin; } +QMap<QByteArray, QByteArray> URLRequestCustomJobDelegate::requestHeaders() const +{ + return m_requestHeaders; +} + void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { if (device) QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::reply, - m_proxy,contentType.toStdString(),device)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::reply, + m_proxy,contentType.toStdString(),device)); } void URLRequestCustomJobDelegate::slotReadyRead() { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::readyRead, m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy)); } void URLRequestCustomJobDelegate::abort() { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::abort, m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy)); } void URLRequestCustomJobDelegate::redirect(const QUrl &url) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::redirect, - m_proxy, toGurl(url))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url))); } void URLRequestCustomJobDelegate::fail(Error error) @@ -129,9 +138,8 @@ void URLRequestCustomJobDelegate::fail(Error error) break; } if (net_error) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::fail, - m_proxy, net_error)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error)); } } diff --git a/src/core/net/url_request_custom_job_delegate.h b/src/core/net/url_request_custom_job_delegate.h index caabfcf99..1bf3ade1f 100644 --- a/src/core/net/url_request_custom_job_delegate.h +++ b/src/core/net/url_request_custom_job_delegate.h @@ -54,6 +54,7 @@ #include "base/memory/ref_counted.h" #include "qtwebenginecoreglobal_p.h" +#include <QMap> #include <QObject> #include <QUrl> @@ -80,6 +81,7 @@ public: QUrl url() const; QByteArray method() const; QUrl initiator() const; + QMap<QByteArray, QByteArray> requestHeaders() const; void reply(const QByteArray &contentType, QIODevice *device); void redirect(const QUrl& url); @@ -93,13 +95,15 @@ private: URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, const QByteArray &method, - const QUrl &initiatorOrigin); + const QUrl &initiatorOrigin, + const QMap<QByteArray, QByteArray> &requestHeaders); friend class URLRequestCustomJobProxy; scoped_refptr<URLRequestCustomJobProxy> m_proxy; QUrl m_request; QByteArray m_method; QUrl m_initiatorOrigin; + const QMap<QByteArray, QByteArray> m_requestHeaders; }; } // namespace diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index b5f10388c..72d14450e 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -152,7 +152,9 @@ void URLRequestCustomJobProxy::readyRead() m_job->notifyReadyRead(); } -void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Optional<url::Origin> initiator) +void URLRequestCustomJobProxy::initialize(GURL url, std::string method, + base::Optional<url::Origin> initiator, + std::map<std::string, std::string> headers) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); Q_ASSERT(!m_delegate); @@ -164,12 +166,16 @@ void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Op QWebEngineUrlSchemeHandler *schemeHandler = nullptr; if (m_profileAdapter) - schemeHandler = m_profileAdapter->customUrlSchemeHandlers()[toQByteArray(m_scheme)]; + schemeHandler = m_profileAdapter->urlSchemeHandler(toQByteArray(m_scheme)); + QMap<QByteArray, QByteArray> qHeaders; + for (auto it = headers.cbegin(); it != headers.cend(); ++it) + qHeaders.insert(toQByteArray(it->first), toQByteArray(it->second)); if (schemeHandler) { m_delegate = new URLRequestCustomJobDelegate(this, toQt(url), QByteArray::fromStdString(method), - initiatorOrigin); + initiatorOrigin, + qHeaders); QWebEngineUrlRequestJob *requestJob = new QWebEngineUrlRequestJob(m_delegate); schemeHandler->requestStarted(requestJob); } diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h index 3986fe119..aa55db07c 100644 --- a/src/core/net/url_request_custom_job_proxy.h +++ b/src/core/net/url_request_custom_job_proxy.h @@ -72,7 +72,7 @@ public: void abort(); void fail(int error); void release(); - void initialize(GURL url, std::string method, base::Optional<url::Origin> initiatorOrigin); + void initialize(GURL url, std::string method, base::Optional<url::Origin> initiatorOrigin, std::map<std::string, std::string> headers); void readyRead(); // IO thread owned: diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp new file mode 100644 index 000000000..6da661cff --- /dev/null +++ b/src/core/net/url_request_notification.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "url_request_notification.h" + +#include "base/supports_user_data.h" +#include "base/task/post_task.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" +#include "net/url_request/url_request.h" +#include "web_contents_adapter_client.h" +#include "web_contents_view_qt.h" +#include "profile_io_data_qt.h" +#include "qwebengineurlrequestinfo_p.h" +#include "type_conversion.h" + +namespace QtWebEngineCore { + +// Calls cancel() when the URLRequest is destroyed. +class UserData : public base::SupportsUserData::Data { +public: + UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} + ~UserData() { m_ptr->cancel(); } + static const char key[]; +private: + URLRequestNotification *m_ptr; +}; + +const char UserData::key[] = "QtWebEngineCore::URLRequestNotification"; + +static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) +{ + return static_cast<content::ResourceType>(resourceType); +} + +URLRequestNotification::URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter) + : m_request(request) + , m_isMainFrameRequest(isMainFrameRequest) + , m_newUrl(newUrl) + , m_originalUrl(requestInfo.requestUrl()) + , m_requestInfo(std::move(requestInfo)) + , m_webContentsGetter(webContentsGetter) + , m_callback(std::move(callback)) + , m_profileAdapter(adapter) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); + + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::UI}, + base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); +} + + +void URLRequestNotification::notify() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // May run concurrently with cancel() so no peeking at m_request here. + + int result = net::OK; + content::WebContents *webContents = m_webContentsGetter.Run(); + + if (webContents) { + + if (m_profileAdapter) { + QWebEngineUrlRequestInterceptor* interceptor = m_profileAdapter->requestInterceptor(); + interceptor->interceptRequest(m_requestInfo); + } + + WebContentsAdapterClient *client = + WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); + + if (!m_requestInfo.changed()) { + client->interceptRequest(m_requestInfo); + } + + if (m_requestInfo.changed()) { + result = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // We handle the rest of the changes later when we are back in I/O thread + } + + // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources + if (result == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { + int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; + client->navigationRequested(m_requestInfo.navigationType(), + m_requestInfo.requestUrl(), + navigationRequestAction, + m_isMainFrameRequest); + result = net::ERR_FAILED; + switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { + case WebContentsAdapterClient::AcceptRequest: + result = net::OK; + break; + case WebContentsAdapterClient::IgnoreRequest: + result = net::ERR_ABORTED; + break; + } + DCHECK(result != net::ERR_FAILED); + } + } + + // Run the callback on the IO thread. + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::IO}, + base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), result)); +} + +void URLRequestNotification::cancel() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + // May run concurrently with notify() but we only touch m_request here. + + m_request = nullptr; +} + +void URLRequestNotification::complete(int error) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_request) { + if (m_requestInfo.changed()) { + if (m_originalUrl != m_requestInfo.d_ptr->url) + *m_newUrl = toGurl(m_requestInfo.d_ptr->url); + + if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { + auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); + for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) + m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } + } + + if (m_request->status().status() != net::URLRequestStatus::CANCELED) + std::move(m_callback).Run(error); + m_request->RemoveUserData(UserData::key); + } + + delete this; +} + +} diff --git a/src/core/net/url_request_notification.h b/src/core/net/url_request_notification.h new file mode 100644 index 000000000..1d9acf12f --- /dev/null +++ b/src/core/net/url_request_notification.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef URL_REQUEST_NOTIFIACTION_H +#define URL_REQUEST_NOTIFIACTION_H + +#include "content/public/browser/resource_request_info.h" +#include "net/base/completion_once_callback.h" +#include "qwebengineurlrequestinfo.h" +#include <QPointer> + +class GURL; + +namespace net { +class URLRequest; +} + +namespace QtWebEngineCore { + +class ProfileAdapter; +class ProfileIoDataQt; + +// Notifies WebContentsAdapterClient of a new URLRequest. +class URLRequestNotification { +public: + URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter); + ~URLRequestNotification() = default; + void cancel(); + void notify(); + void complete(int error); + +private: + net::URLRequest *m_request; //used only by io thread + bool m_isMainFrameRequest; + GURL *m_newUrl; + const QUrl m_originalUrl; + QWebEngineUrlRequestInfo m_requestInfo; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; + net::CompletionOnceCallback m_callback; + QPointer<ProfileAdapter> m_profileAdapter; +}; +} +#endif diff --git a/src/core/net/url_request_qrc_job_qt.cpp b/src/core/net/url_request_qrc_job_qt.cpp deleted file mode 100644 index a2712653d..000000000 --- a/src/core/net/url_request_qrc_job_qt.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "url_request_qrc_job_qt.h" - -#include "type_conversion.h" - -#include "base/pending_task.h" -#include "base/threading/thread_task_runner_handle.h" -#include "net/base/net_errors.h" -#include "net/base/io_buffer.h" - -#include <QUrl> -#include <QFileInfo> -#include <QMimeDatabase> -#include <QMimeType> - -using namespace net; -namespace QtWebEngineCore { - -URLRequestQrcJobQt::URLRequestQrcJobQt(URLRequest *request, NetworkDelegate *networkDelegate) - : URLRequestJob(request, networkDelegate) - , m_remainingBytes(0) - , m_weakFactory(this) -{ -} - -URLRequestQrcJobQt::~URLRequestQrcJobQt() -{ - if (m_file.isOpen()) - m_file.close(); -} - -void URLRequestQrcJobQt::Start() -{ - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&URLRequestQrcJobQt::startGetHead, m_weakFactory.GetWeakPtr())); -} - -void URLRequestQrcJobQt::Kill() -{ - if (m_file.isOpen()) - m_file.close(); - m_weakFactory.InvalidateWeakPtrs(); - - URLRequestJob::Kill(); -} - -bool URLRequestQrcJobQt::GetMimeType(std::string *mimeType) const -{ - DCHECK(request_); - if (m_mimeType.size() > 0) { - *mimeType = m_mimeType; - return true; - } - return false; -} - -int URLRequestQrcJobQt::ReadRawData(IOBuffer *buf, int bufSize) -{ - DCHECK_GE(m_remainingBytes, 0); - // File has been read finished. - if (!m_remainingBytes || !bufSize) { - return 0; - } - if (m_remainingBytes < bufSize) - bufSize = static_cast<int>(m_remainingBytes); - qint64 rv = m_file.read(buf->data(), bufSize); - if (rv >= 0) { - m_remainingBytes -= rv; - DCHECK_GE(m_remainingBytes, 0); - return static_cast<int>(rv); - } - return static_cast<int>(rv); -} - -void URLRequestQrcJobQt::startGetHead() -{ - // Get qrc file path. - QString qrcFilePath = ':' + toQt(request_->url()).path(); - m_file.setFileName(qrcFilePath); - QFileInfo qrcFileInfo(m_file); - // Get qrc file mime type. - QMimeDatabase mimeDatabase; - QMimeType mimeType = mimeDatabase.mimeTypeForFile(qrcFileInfo); - m_mimeType = mimeType.name().toStdString(); - // Open file - if (m_file.open(QIODevice::ReadOnly)) { - m_remainingBytes = m_file.size(); - set_expected_content_size(m_remainingBytes); - // Notify that the headers are complete - NotifyHeadersComplete(); - } else { - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_INVALID_URL)); - } -} - -} // namespace QtWebEngineCore diff --git a/src/core/net/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp index 918500b58..ec36e70d9 100644 --- a/src/core/net/webui_controller_factory_qt.cpp +++ b/src/core/net/webui_controller_factory_qt.cpp @@ -48,6 +48,7 @@ #include "base/location.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" +#include "chrome/browser/accessibility/accessibility_ui.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/devtools_ui.h" #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h" @@ -136,6 +137,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI *web_ui, Profile *profile, co // return nullptr; return &NewWebUI<DevToolsUI>; } + if (url.host() == chrome::kChromeUIAccessibilityHost) + return &NewWebUI<AccessibilityUI>; // if (url.host_piece() == chrome::kChromeUIUserActionsHost) // return &NewWebUI<UserActionsUI>; diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp index ec49414b0..c4a075544 100644 --- a/src/core/ozone/gl_context_qt.cpp +++ b/src/core/ozone/gl_context_qt.cpp @@ -119,14 +119,21 @@ void* GLContextHelper::getGlXConfig() void* GLContextHelper::getEGLDisplay() { +#ifdef Q_OS_WIN + // Windows QPA plugin does not implement resourceForIntegration for "egldisplay". + // Use resourceForContext instead. + return resourceForContext(QByteArrayLiteral("egldisplay")); +#else return resourceForIntegration(QByteArrayLiteral("egldisplay")); +#endif } void* GLContextHelper::getXDisplay() { - if (QGuiApplication::platformName() != QLatin1String("xcb")) - return nullptr; - return qApp->platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("display"), qApp->primaryScreen()); + QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); + if (pni) + return pni->nativeResourceForScreen(QByteArrayLiteral("display"), qApp->primaryScreen()); + return nullptr; } void* GLContextHelper::getNativeDisplay() diff --git a/src/core/ozone/gl_surface_egl_qt.cpp b/src/core/ozone/gl_surface_egl_qt.cpp index 9fe5985ce..8715a5095 100644 --- a/src/core/ozone/gl_surface_egl_qt.cpp +++ b/src/core/ozone/gl_surface_egl_qt.cpp @@ -44,9 +44,9 @@ #include "gl_context_qt.h" #include "ozone/gl_surface_egl_qt.h" -#include "ui/gl/gl_surface_egl.h" #if !defined(OS_MACOSX) #include "ui/gl/egl_util.h" +#include "ui/gl/gl_surface_egl.h" #include "ui/gl/init/gl_factory.h" // From ANGLE's egl/eglext.h. @@ -302,10 +302,6 @@ void* GLSurfacelessQtEGL::GetShareHandle() return NULL; } -} // namespace gl -#endif // !defined(OS_MACOSX) - -namespace gl { std::string DriverEGL::GetPlatformExtensions() { EGLDisplay display = GLContextHelper::getEGLDisplay(); @@ -317,3 +313,15 @@ std::string DriverEGL::GetPlatformExtensions() return str ? std::string(str) : ""; } } // namespace gl +#else +namespace gl { +struct GL_EXPORT DriverEGL { + static std::string GetPlatformExtensions(); +}; + +std::string DriverEGL::GetPlatformExtensions() +{ + return ""; +} +} // namespace gl +#endif // !defined(OS_MACOSX) diff --git a/src/core/ozone/gl_surface_qt.cpp b/src/core/ozone/gl_surface_qt.cpp index 7cde289ae..551ba888c 100644 --- a/src/core/ozone/gl_surface_qt.cpp +++ b/src/core/ozone/gl_surface_qt.cpp @@ -64,7 +64,6 @@ #include "ozone/gl_surface_wgl_qt.h" #include "gpu/ipc/service/direct_composition_surface_win.h" -#include "ui/gl/gl_context_wgl.h" #include "ui/gl/vsync_provider_win.h" #endif @@ -141,9 +140,6 @@ bool InitializeGLOneOffPlatform() { VSyncProviderWin::InitializeOneOff(); - if (GetGLImplementation() == kGLImplementationOSMesaGL) - return false; - if (GetGLImplementation() == kGLImplementationEGLGLES2) return GLSurfaceEGLQt::InitializeOneOff(); diff --git a/src/core/ozone/gl_surface_wgl_qt.cpp b/src/core/ozone/gl_surface_wgl_qt.cpp index 7c9e87b86..ac27a9c20 100644 --- a/src/core/ozone/gl_surface_wgl_qt.cpp +++ b/src/core/ozone/gl_surface_wgl_qt.cpp @@ -37,9 +37,9 @@ ** ****************************************************************************/ -#if defined(OS_WIN) - #include "gl_surface_wgl_qt.h" + +#if defined(OS_WIN) #include "ui/gl/gl_surface_wgl.h" namespace gl { diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp index 905e8f403..1115f3fac 100644 --- a/src/core/ozone/ozone_platform_qt.cpp +++ b/src/core/ozone/ozone_platform_qt.cpp @@ -43,6 +43,7 @@ #include "ozone/surface_factory_qt.h" #include "ozone/platform_window_qt.h" #include "ui/display/types/native_display_delegate.h" +#include "ui/events/system_input_injector.h" #include "ui/ozone/common/stub_client_native_pixmap_factory.h" #include "ui/ozone/common/stub_overlay_manager.h" #include "ui/ozone/public/cursor_factory_ozone.h" diff --git a/src/core/ozone/platform_window_qt.h b/src/core/ozone/platform_window_qt.h index b712b706a..bb2fc714b 100644 --- a/src/core/ozone/platform_window_qt.h +++ b/src/core/ozone/platform_window_qt.h @@ -75,6 +75,9 @@ public: void MoveCursorTo(const gfx::Point&) override { } void ConfineCursorToBounds(const gfx::Rect&) override { } PlatformImeController* GetPlatformImeController() override { return nullptr; } + void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override { } + gfx::Rect GetRestoredBoundsInPixels() const override { return gfx::Rect(); } + // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp index b999f4186..2a7311187 100644 --- a/src/core/permission_manager_qt.cpp +++ b/src/core/permission_manager_qt.cpp @@ -61,8 +61,9 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) return ProfileAdapter::AudioCapturePermission; case content::PermissionType::VIDEO_CAPTURE: return ProfileAdapter::VideoCapturePermission; - case content::PermissionType::FLASH: case content::PermissionType::NOTIFICATIONS: + return ProfileAdapter::NotificationPermission; + case content::PermissionType::FLASH: case content::PermissionType::MIDI_SYSEX: case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER: case content::PermissionType::MIDI: @@ -76,6 +77,7 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) case content::PermissionType::CLIPBOARD_WRITE: return ProfileAdapter::ClipboardWrite; case content::PermissionType::PAYMENT_HANDLER: + case content::PermissionType::BACKGROUND_FETCH: case content::PermissionType::NUM: break; } @@ -187,6 +189,9 @@ int PermissionManagerQt::RequestPermission(content::PermissionType permission, m_requests.insert(request_id, request); if (permissionType == ProfileAdapter::GeolocationPermission) contentsDelegate->requestGeolocationPermission(request.origin); + else if (permissionType == ProfileAdapter::NotificationPermission) + contentsDelegate->requestUserNotificationPermission(request.origin); + return request_id; } @@ -235,6 +240,8 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio const ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::GeolocationPermission) contentsDelegate->requestGeolocationPermission(request.origin); + else if (permissionType == ProfileAdapter::NotificationPermission) + contentsDelegate->requestUserNotificationPermission(request.origin); } return request_id; } @@ -294,8 +301,8 @@ void PermissionManagerQt::ResetPermission( int PermissionManagerQt::SubscribePermissionStatusChange( content::PermissionType permission, + content::RenderFrameHost * /* render_frame_host */, const GURL& requesting_origin, - const GURL& /*embedding_origin*/, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) { int subscriber_id = ++m_subscriberIdCount; @@ -311,7 +318,7 @@ int PermissionManagerQt::SubscribePermissionStatusChange( void PermissionManagerQt::UnsubscribePermissionStatusChange(int subscription_id) { if (!m_subscribers.remove(subscription_id)) - qWarning() << "PermissionManagerQt::UnsubscribePermissionStatusChange called on unknown subscription id" << subscription_id; + LOG(WARNING) << "PermissionManagerQt::UnsubscribePermissionStatusChange called on unknown subscription id" << subscription_id; } } // namespace QtWebEngineCore diff --git a/src/core/permission_manager_qt.h b/src/core/permission_manager_qt.h index b3bd3dc7a..89eb6cf85 100644 --- a/src/core/permission_manager_qt.h +++ b/src/core/permission_manager_qt.h @@ -92,8 +92,8 @@ public: int SubscribePermissionStatusChange( content::PermissionType permission, + content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - const GURL& embedding_origin, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) override; void UnsubscribePermissionStatusChange(int subscription_id) override; diff --git a/src/core/platform_notification_service_qt.cpp b/src/core/platform_notification_service_qt.cpp new file mode 100644 index 000000000..dff3aed61 --- /dev/null +++ b/src/core/platform_notification_service_qt.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "platform_notification_service_qt.h" + +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/permission_type.h" +#include "content/public/browser/notification_event_dispatcher.h" +#include "ui/message_center/public/cpp/notification_delegate.h" + +#include "profile_adapter.h" +#include "profile_adapter_client.h" +#include "profile_qt.h" +#include "user_notification_controller.h" +#include "resource_context_qt.h" +#include "type_conversion.h" + +#include <QSharedPointer> + +namespace QtWebEngineCore { + +struct NonPersistentNotificationDelegate : UserNotificationController::Delegate { + NonPersistentNotificationDelegate(const std::string &id) : notification_id(id) { } + + virtual void shown() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentShowEvent(notification_id); + } + + virtual void clicked() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentClickEvent(notification_id, base::DoNothing()); + } + + virtual void closed(bool /*by_user*/) override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentCloseEvent(notification_id, base::DoNothing()); + } + + const std::string notification_id; +}; + +struct PersistentNotificationDelegate : UserNotificationController::Delegate { + PersistentNotificationDelegate(content::BrowserContext *context, const std::string &id, const GURL &origin) + : browser_context(context), notification_id(id), origin(origin) { } + + virtual void shown() override { } + + virtual void clicked() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNotificationClickEvent(browser_context, notification_id, origin, base::nullopt, base::nullopt, base::DoNothing()); + } + + virtual void closed(bool by_user) override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNotificationCloseEvent(browser_context, notification_id, origin, by_user, base::DoNothing()); + } + + content::BrowserContext *browser_context; + const std::string notification_id; + const GURL origin; +}; + + +PlatformNotificationServiceQt::PlatformNotificationServiceQt() {} + +PlatformNotificationServiceQt::~PlatformNotificationServiceQt() {} + +void PlatformNotificationServiceQt::DisplayNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id, + const GURL &origin, + const blink::PlatformNotificationData ¬ificationData, + const blink::NotificationResources ¬ificationResources) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + auto delegate = new NonPersistentNotificationDelegate(notification_id); + QSharedPointer<UserNotificationController> controller( + new UserNotificationController(notificationData, notificationResources, origin, delegate)); + + profile->profileAdapter()->ephemeralNotifications().insert(QByteArray::fromStdString(notification_id), controller); + + const QList<ProfileAdapterClient *> clients = profile->profileAdapter()->clients(); + for (ProfileAdapterClient *client : clients) + client->showNotification(controller); +} + +void PlatformNotificationServiceQt::DisplayPersistentNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id, + const GURL &service_worker_origin, + const GURL &origin, + const blink::PlatformNotificationData ¬ificationData, + const blink::NotificationResources ¬ificationResources) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt * profile = static_cast<ProfileQt*>(browser_context); + + auto delegate = new PersistentNotificationDelegate(profile, notification_id, service_worker_origin); + QSharedPointer<UserNotificationController> controller( + new UserNotificationController(notificationData, notificationResources, service_worker_origin, delegate)); + + profile->profileAdapter()->persistentNotifications().insert(QByteArray::fromStdString(notification_id), controller); + const QList<ProfileAdapterClient *> clients = profile->profileAdapter()->clients(); + for (ProfileAdapterClient *client : clients) + client->showNotification(controller); +} + +void PlatformNotificationServiceQt::CloseNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + QSharedPointer<UserNotificationController> notificationController = + profile->profileAdapter()->ephemeralNotifications().take(QByteArray::fromStdString(notification_id)).lock(); + if (notificationController) + notificationController->closeNotification(); +} + +void PlatformNotificationServiceQt::ClosePersistentNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + QSharedPointer<UserNotificationController> notificationController = + profile->profileAdapter()->persistentNotifications().take(QByteArray::fromStdString(notification_id)); + if (notificationController) + notificationController->closeNotification(); +} + +void PlatformNotificationServiceQt::GetDisplayedNotifications( + content::BrowserContext *browser_context, + const DisplayedNotificationsCallback &callback) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt *>(browser_context); + + std::unique_ptr<std::set<std::string>> movableStdStringSet = std::make_unique<std::set<std::string>>(); + auto it = profile->profileAdapter()->persistentNotifications().constBegin(); + const auto end = profile->profileAdapter()->persistentNotifications().constEnd(); + while (it != end) { + if (it.value()->isShown()) + movableStdStringSet->insert(it.key().toStdString()); + ++it; + } + + callback.Run(std::move(movableStdStringSet), true /* supports_synchronization */); +} + +int64_t PlatformNotificationServiceQt::ReadNextPersistentNotificationId(content::BrowserContext *browser_context) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + auto prefs = static_cast<ProfileQt *>(browser_context)->GetPrefs(); + int64_t nextId = prefs->GetInteger(prefs::kNotificationNextPersistentId) + 1; + prefs->SetInt64(prefs::kNotificationNextPersistentId, nextId); + return nextId; +} + +} // namespace QtWebEngineCore diff --git a/src/core/platform_notification_service_qt.h b/src/core/platform_notification_service_qt.h new file mode 100644 index 000000000..66cee9ed0 --- /dev/null +++ b/src/core/platform_notification_service_qt.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLATFORM_NOTIFICATION_SERVICE_QT_H +#define PLATFORM_NOTIFICATION_SERVICE_QT_H + +#include "content/public/browser/platform_notification_service.h" + +namespace QtWebEngineCore { + +class PlatformNotificationServiceQt : public content::PlatformNotificationService { +public: + PlatformNotificationServiceQt(); + ~PlatformNotificationServiceQt() override; + + // Displays the notification described in |notification_data| to the user. A + // closure through which the notification can be closed will be stored in the + // |cancel_callback| argument. This method must be called on the UI thread. + void DisplayNotification(content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const blink::PlatformNotificationData& notificationData, + const blink::NotificationResources& notificationResources) override; + + // Displays the persistent notification described in |notification_data| to + // the user. This method must be called on the UI thread. + void DisplayPersistentNotification(content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_origin, + const GURL& origin, + const blink::PlatformNotificationData& notification_data, + const blink::NotificationResources& notification_resources) override; + + // Closes the notification identified by |notification_id|. + // This method must be called on the UI thread. + void CloseNotification(content::BrowserContext* browser_context, const std::string& notification_id) override; + + // Closes the persistent notification identified by |persistent_notification_id|. + // This method must be called on the UI thread. + void ClosePersistentNotification(content::BrowserContext* browser_context, const std::string& notification_id) override; + + // Retrieves the ids of all currently displaying notifications and + // posts |callback| with the result. + void GetDisplayedNotifications(content::BrowserContext* browser_context, const DisplayedNotificationsCallback& callback) override; + + // Reads the value of the next persistent notification ID from the profile and + // increments the value, as it is called once per notification write. + virtual int64_t ReadNextPersistentNotificationId(content::BrowserContext* browser_context) override; + + // Records a given notification to UKM. + virtual void RecordNotificationUkmEvent(content::BrowserContext*, const content::NotificationDatabaseData&) override { } +}; + +} // namespace QtWebEngineCore + +#endif // PLATFORM_NOTIFICATION_SERVICE_QT_H diff --git a/src/core/printing/pdfium_document_wrapper_qt.cpp b/src/core/printing/pdfium_document_wrapper_qt.cpp index a18258d0e..6dbdf64ef 100644 --- a/src/core/printing/pdfium_document_wrapper_qt.cpp +++ b/src/core/printing/pdfium_document_wrapper_qt.cpp @@ -54,7 +54,6 @@ public: : m_pageData(FPDF_LoadPage(data, pageIndex)) , m_width(FPDF_GetPageWidth(m_pageData)) , m_height(FPDF_GetPageHeight(m_pageData)) - , m_index(pageIndex) , m_image(createImage(targetWidth, targetHeight)) { } @@ -63,7 +62,6 @@ public: : m_pageData(nullptr) , m_width(-1) , m_height(-1) - , m_index(-1) , m_image(QImage()) { } @@ -102,14 +100,13 @@ private: 0, 0); FPDFBitmap_Destroy(bitmap); bitmap = nullptr; - return std::move(image); + return image; } private: FPDF_PAGE m_pageData; int m_width; int m_height; - int m_index; QImage m_image; }; diff --git a/src/core/printing/print_view_manager_base_qt.cpp b/src/core/printing/print_view_manager_base_qt.cpp index 0e7239ef8..1a16be69e 100644 --- a/src/core/printing/print_view_manager_base_qt.cpp +++ b/src/core/printing/print_view_manager_base_qt.cpp @@ -52,6 +52,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/post_task.h" #include "base/timer/timer.h" #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" @@ -59,12 +60,13 @@ #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/printer_query.h" #include "components/printing/common/print_messages.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" -#include "printing/pdf_metafile_skia.h" +#include "printing/metafile_skia.h" #include "printing/print_job_constants.h" #include "printing/printed_document.h" @@ -123,8 +125,8 @@ void PrintViewManagerBaseQt::PrintDocument(printing::PrintedDocument *document, const gfx::Rect &content_area, const gfx::Point &offsets) { - std::unique_ptr<printing::PdfMetafileSkia> metafile = - std::make_unique<printing::PdfMetafileSkia>(); + std::unique_ptr<printing::MetafileSkia> metafile = + std::make_unique<printing::MetafileSkia>(); CHECK(metafile->InitFromData(print_data->front(), print_data->size())); // Update the rendered document. It will send notifications to the listener. @@ -155,21 +157,19 @@ void PrintViewManagerBaseQt::OnDidPrintDocument(content::RenderFrameHost* /*rend return; const PrintHostMsg_DidPrintContent_Params &content = params.content; - if (!base::SharedMemory::IsHandleValid(content.metafile_data_handle)) { + if (!content.metafile_data_region.IsValid()) { NOTREACHED() << "invalid memory handle"; web_contents()->Stop(); return; } - std::unique_ptr<base::SharedMemory> shared_buf = - std::make_unique<base::SharedMemory>(content.metafile_data_handle, true); - if (!shared_buf->Map(content.data_size)) { + auto data = base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(content.metafile_data_region); + if (!data) { NOTREACHED() << "couldn't map"; web_contents()->Stop(); return; } - auto data = base::MakeRefCounted<base::RefCountedSharedMemory>( - std::move(shared_buf), content.data_size); + PrintDocument(document, data, params.page_size, params.content_area, params.physical_offsets); } @@ -516,9 +516,8 @@ void PrintViewManagerBaseQt::ReleasePrinterQuery() printerQuery = m_printerQueriesQueue->PopPrinterQuery(cookie); if (!printerQuery.get()) return; - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&printing::PrinterQuery::StopWorker, printerQuery.get())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&printing::PrinterQuery::StopWorker, printerQuery.get())); } // Originally from print_preview_message_handler.cc: @@ -528,7 +527,7 @@ void PrintViewManagerBaseQt::StopWorker(int documentCookie) { scoped_refptr<printing::PrinterQuery> printer_query = m_printerQueriesQueue->PopPrinterQuery(documentCookie); if (printer_query.get()) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, base::BindOnce(&printing::PrinterQuery::StopWorker, printer_query)); } } diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp index ff9fc76be..e88f48624 100644 --- a/src/core/printing/print_view_manager_qt.cpp +++ b/src/core/printing/print_view_manager_qt.cpp @@ -53,50 +53,43 @@ #include "base/values.h" #include "base/memory/ref_counted_memory.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/printer_query.h" #include "components/printing/common/print_messages.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/common/web_preferences.h" -#include "printing/pdf_metafile_skia.h" +#include "printing/metafile_skia.h" #include "printing/print_job_constants.h" #include "printing/units.h" -DEFINE_WEB_CONTENTS_USER_DATA_KEY(QtWebEngineCore::PrintViewManagerQt); - namespace { static const qreal kMicronsToMillimeter = 1000.0f; -static std::vector<char> -GetStdVectorFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +static QSharedPointer<QByteArray> GetStdVectorFromHandle(const base::ReadOnlySharedMemoryRegion &handle) { - std::unique_ptr<base::SharedMemory> shared_buf( - new base::SharedMemory(handle, true)); - - if (!shared_buf->Map(data_size)) { - return std::vector<char>(); - } + base::ReadOnlySharedMemoryMapping map = handle.Map(); + if (!map.IsValid()) + return QSharedPointer<QByteArray>(new QByteArray); - char* data = static_cast<char*>(shared_buf->memory()); - return std::vector<char>(data, data + data_size); + const char* data = static_cast<const char*>(map.memory()); + return QSharedPointer<QByteArray>(new QByteArray(data, map.size())); } static scoped_refptr<base::RefCountedBytes> -GetBytesFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +GetBytesFromHandle(const base::ReadOnlySharedMemoryRegion &handle) { - std::unique_ptr<base::SharedMemory> shared_buf(new base::SharedMemory(handle, true)); - - if (!shared_buf->Map(data_size)) { + base::ReadOnlySharedMemoryMapping map = handle.Map(); + if (!map.IsValid()) return nullptr; - } - unsigned char* data = static_cast<unsigned char*>(shared_buf->memory()); - std::vector<unsigned char> dataVector(data, data + data_size); + const unsigned char* data = static_cast<const unsigned char*>(map.memory()); + std::vector<unsigned char> dataVector(data, data + map.size()); return base::RefCountedBytes::TakeVector(&dataVector); } @@ -108,15 +101,14 @@ static void SavePdfFile(scoped_refptr<base::RefCountedBytes> data, base::AssertBlockingAllowed(); DCHECK_GT(data->size(), 0U); - printing::PdfMetafileSkia metafile; + printing::MetafileSkia metafile; metafile.InitFromData(static_cast<const void*>(data->front()), data->size()); base::File file(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); bool success = file.IsValid() && metafile.SaveTo(&file); - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(saveCallback, success)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(saveCallback, success)); } static base::DictionaryValue *createPrintSettings() @@ -222,16 +214,16 @@ void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayou return; if (m_printSettings || !filePath.length()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, false)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); return; } m_pdfOutputPath = toFilePath(filePath); m_pdfSaveCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor)) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, false)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); resetPdfState(); } } @@ -246,16 +238,15 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, // If there already is a pending print in progress, don't try starting another one. if (m_printSettings) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); return; } m_pdfPrintCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor, useCustomMargins)) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); resetPdfState(); } @@ -358,21 +349,18 @@ void PrintViewManagerQt::OnMetafileReadyForPrinting(content::RenderFrameHost* rf StopWorker(params.document_cookie); // Create local copies so we can reset the state and take a new pdf print job. - base::Callback<void(const std::vector<char>&)> pdf_print_callback = m_pdfPrintCallback; - base::Callback<void(bool)> pdf_save_callback = m_pdfSaveCallback; + PrintToPDFCallback pdf_print_callback = std::move(m_pdfPrintCallback); + PrintToPDFFileCallback pdf_save_callback = std::move(m_pdfSaveCallback); base::FilePath pdfOutputPath = m_pdfOutputPath; resetPdfState(); if (!pdf_print_callback.is_null()) { - std::vector<char> data_vector = GetStdVectorFromHandle(params.content.metafile_data_handle, - params.content.data_size); - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(pdf_print_callback, data_vector)); + QSharedPointer<QByteArray> data_array = GetStdVectorFromHandle(params.content.metafile_data_region); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(pdf_print_callback, data_array)); } else { - scoped_refptr<base::RefCountedBytes> data_bytes - = GetBytesFromHandle(params.content.metafile_data_handle, params.content.data_size); + scoped_refptr<base::RefCountedBytes> data_bytes = GetBytesFromHandle(params.content.metafile_data_region); base::PostTaskWithTraits(FROM_HERE, {base::MayBlock()}, base::BindOnce(&SavePdfFile, data_bytes, pdfOutputPath, pdf_save_callback)); } @@ -392,9 +380,8 @@ void PrintViewManagerQt::DidStartLoading() void PrintViewManagerQt::NavigationStopped() { if (!m_pdfPrintCallback.is_null()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); PrintViewManagerBaseQt::NavigationStopped(); @@ -404,9 +391,8 @@ void PrintViewManagerQt::RenderProcessGone(base::TerminationStatus status) { PrintViewManagerBaseQt::RenderProcessGone(status); if (!m_pdfPrintCallback.is_null()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); } diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h index 209be8782..b21389691 100644 --- a/src/core/printing/print_view_manager_qt.h +++ b/src/core/printing/print_view_manager_qt.h @@ -56,6 +56,8 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_user_data.h" +#include <QSharedPointer> + struct PrintHostMsg_RequestPrintPreview_Params; struct PrintHostMsg_DidPreviewDocument_Params; @@ -83,7 +85,7 @@ class PrintViewManagerQt { public: ~PrintViewManagerQt() override; - typedef base::Callback<void(const std::vector<char> &result)> PrintToPDFCallback; + typedef base::Callback<void(QSharedPointer<QByteArray> result)> PrintToPDFCallback; typedef base::Callback<void(bool success)> PrintToPDFFileCallback; // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath. diff --git a/src/core/process_main.cpp b/src/core/process_main.cpp index 677f0b10a..d661d3b90 100644 --- a/src/core/process_main.cpp +++ b/src/core/process_main.cpp @@ -44,7 +44,10 @@ #if defined(OS_WIN) #include "sandbox/win/src/sandbox_types.h" #include "content/public/app/sandbox_helper_win.h" -#endif // OS_WIN +#elif defined(OS_MACOSX) +#include "base/logging.h" +#include "sandbox/mac/seatbelt_exec.h" +#endif namespace QtWebEngine { @@ -64,6 +67,13 @@ int processMain(int argc, const char **argv) params.argc = argc; params.argv = argv; #endif // OS_WIN +#if defined(OS_MACOSX) + sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt = + sandbox::SeatbeltExecServer::CreateFromArguments(argv[0], argc, const_cast<char**>(argv)); + if (seatbelt.sandbox_required) { + CHECK(seatbelt.server->InitializeSandbox()); + } +#endif // defined(OS_MACOSX) return content::ContentMain(params); } diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 1b946949a..7070292d6 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -44,10 +44,12 @@ #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/download_manager.h" +#include "api/qwebengineurlscheme.h" #include "content_client_qt.h" #include "download_manager_delegate_qt.h" #include "net/url_request_context_getter_qt.h" #include "permission_manager_qt.h" +#include "profile_adapter_client.h" #include "profile_qt.h" #include "renderer_host/user_resource_controller_host.h" #include "type_conversion.h" @@ -57,6 +59,10 @@ #include "components/keyed_service/content/browser_context_dependency_manager.h" +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/browser/extension_system.h" +#endif + #include <QCoreApplication> #include <QDir> #include <QString> @@ -78,10 +84,12 @@ namespace QtWebEngineCore { ProfileAdapter::ProfileAdapter(const QString &storageName): m_name(storageName) , m_offTheRecord(storageName.isEmpty()) + , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) , m_httpCacheType(DiskHttpCache) , m_persistentCookiesPolicy(AllowPersistentCookies) , m_visitedLinksPolicy(TrackVisitedLinksOnDisk) , m_httpCacheMaxSize(0) + , m_pageRequestInterceptors(0) { WebEngineContext::current()->addProfileAdapter(this); // creation of profile requires webengine context @@ -89,6 +97,11 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): content::BrowserContext::Initialize(m_profile.data(), toFilePath(dataPath())); // fixme: this should not be here m_profile->m_profileIOData->initializeOnUIThread(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!storageName.isEmpty()) + extensions::ExtensionSystem::Get(m_profile.data())->InitForRegularProfile(true); +#endif } ProfileAdapter::~ProfileAdapter() @@ -101,6 +114,8 @@ ProfileAdapter::~ProfileAdapter() m_profile->GetDownloadManager(m_profile.data())->Shutdown(); m_downloadManagerDelegate.reset(); } + delete m_clientCertificateStore; + Q_ASSERT(m_pageRequestInterceptors == 0); } void ProfileAdapter::setStorageName(const QString &storageName) @@ -177,6 +192,22 @@ void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient) m_clients.removeOne(adapterClient); } +void ProfileAdapter::addPageRequestInterceptor() +{ + ++m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + +void ProfileAdapter::removePageRequestInterceptor() +{ + Q_ASSERT(m_pageRequestInterceptors > 0); + --m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + + void ProfileAdapter::cancelDownload(quint32 downloadId) { downloadManagerDelegate()->cancelDownload(downloadId); @@ -237,6 +268,11 @@ void ProfileAdapter::setDataPath(const QString &path) } } +void ProfileAdapter::setDownloadPath(const QString &path) +{ + m_downloadPath = path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : path; +} + QString ProfileAdapter::cachePath() const { if (m_offTheRecord) @@ -405,9 +441,23 @@ void ProfileAdapter::setHttpCacheMaxSize(int maxSize) m_profile->m_profileIOData->updateHttpCache(); } -const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &ProfileAdapter::customUrlSchemeHandlers() const +static bool isInternalScheme(const QByteArray &scheme) { - return m_customUrlSchemeHandlers; + static QSet<QByteArray> internalSchemes{ + QByteArrayLiteral("qrc"), + QByteArrayLiteral("data"), + QByteArrayLiteral("blob"), + QByteArrayLiteral("http"), + QByteArrayLiteral("https"), + QByteArrayLiteral("ftp"), + QByteArrayLiteral("javascript"), + }; + return internalSchemes.contains(scheme); +} + +QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme) +{ + return m_customUrlSchemeHandlers.value(scheme.toLower()).data(); } const QList<QByteArray> ProfileAdapter::customUrlSchemes() const @@ -421,12 +471,17 @@ void ProfileAdapter::updateCustomUrlSchemeHandlers() m_profile->m_profileIOData->updateJobFactory(); } -bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { + Q_ASSERT(handler); bool removedOneOrMore = false; auto it = m_customUrlSchemeHandlers.begin(); while (it != m_customUrlSchemeHandlers.end()) { if (it.value() == handler) { + if (isInternalScheme(it.key())) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", it.key().constData()); + continue; + } it = m_customUrlSchemeHandlers.erase(it); removedOneOrMore = true; continue; @@ -435,26 +490,43 @@ bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *ha } if (removedOneOrMore) updateCustomUrlSchemeHandlers(); - return removedOneOrMore; } -QWebEngineUrlSchemeHandler *ProfileAdapter::takeCustomUrlSchemeHandler(const QByteArray &scheme) +void ProfileAdapter::removeUrlScheme(const QByteArray &scheme) { - QWebEngineUrlSchemeHandler *handler = m_customUrlSchemeHandlers.take(scheme); - if (handler) + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.remove(canonicalScheme)) updateCustomUrlSchemeHandlers(); - return handler; } -void ProfileAdapter::addCustomUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { - m_customUrlSchemeHandlers.insert(scheme, handler); + Q_ASSERT(handler); + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) { + qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + return; + } + if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) + qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " + "before installing the custom scheme handler.", scheme.constData()); + + m_customUrlSchemeHandlers.insert(canonicalScheme, handler); updateCustomUrlSchemeHandlers(); } -void ProfileAdapter::clearCustomUrlSchemeHandlers() +void ProfileAdapter::removeAllUrlSchemeHandlers() { m_customUrlSchemeHandlers.clear(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); updateCustomUrlSchemeHandlers(); } @@ -563,7 +635,44 @@ void ProfileAdapter::removeWebContentsAdapterClient(WebContentsAdapterClient *cl void ProfileAdapter::resetVisitedLinksManager() { - m_visitedLinksManager.reset(new VisitedLinksManagerQt(this)); + m_visitedLinksManager.reset(new VisitedLinksManagerQt(m_profile.data(), persistVisitedLinks())); +} + +void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable) +{ + if (m_usedForGlobalCertificateVerification == enable) + return; + + static QPointer<ProfileAdapter> profileForglobalCertificateVerification; + + m_usedForGlobalCertificateVerification = enable; + if (enable) { + if (profileForglobalCertificateVerification) { + profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false; + for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients)) + client->useForGlobalCertificateVerificationChanged(); + } + profileForglobalCertificateVerification = this; + } else { + Q_ASSERT(profileForglobalCertificateVerification); + Q_ASSERT(profileForglobalCertificateVerification == this); + profileForglobalCertificateVerification = nullptr; + } + + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateUsedForGlobalCertificateVerification(); +} + +bool ProfileAdapter::isUsedForGlobalCertificateVerification() const +{ + return m_usedForGlobalCertificateVerification; +} + +QWebEngineClientCertificateStore *ProfileAdapter::clientCertificateStore() +{ + if (!m_clientCertificateStore) + m_clientCertificateStore = new QWebEngineClientCertificateStore(m_profile->m_profileIOData->clientCertificateStoreData()); + return m_clientCertificateStore; } } // namespace QtWebEngineCore diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 7ed5c13f5..1aff145b2 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -60,16 +60,19 @@ #include <QString> #include <QVector> +#include "api/qwebengineclientcertificatestore.h" #include "api/qwebenginecookiestore.h" #include "api/qwebengineurlrequestinterceptor.h" #include "api/qwebengineurlschemehandler.h" +#include "net/qrc_url_scheme_handler.h" QT_FORWARD_DECLARE_CLASS(QObject) namespace QtWebEngineCore { -class ProfileAdapterClient; +class UserNotificationController; class DownloadManagerDelegateQt; +class ProfileAdapterClient; class ProfileQt; class UserResourceControllerHost; class VisitedLinksManagerQt; @@ -113,6 +116,9 @@ public: QString dataPath() const; void setDataPath(const QString &path); + QString downloadPath() const { return m_downloadPath; } + void setDownloadPath(const QString &path); + QString cachePath() const; void setCachePath(const QString &path); @@ -153,8 +159,7 @@ public: enum PermissionType { UnsupportedPermission = 0, GeolocationPermission = 1, -// Reserved: -// NotificationPermission = 2, + NotificationPermission = 2, AudioCapturePermission = 3, VideoCapturePermission = 4, ClipboardRead = 5, @@ -174,14 +179,14 @@ public: void setHttpCacheMaxSize(int maxSize); bool trackVisitedLinks() const; - bool persistVisitedLinks() const; - const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &customUrlSchemeHandlers() const; + QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &scheme); + void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler); + void removeUrlScheme(const QByteArray &scheme); + void removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler); + void removeAllUrlSchemeHandlers(); + const QList<QByteArray> customUrlSchemes() const; - void clearCustomUrlSchemeHandlers(); - void addCustomUrlSchemeHandler(const QByteArray &, QWebEngineUrlSchemeHandler *); - bool removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *); - QWebEngineUrlSchemeHandler *takeCustomUrlSchemeHandler(const QByteArray &); UserResourceControllerHost *userResourceController(); void permissionRequestReply(const QUrl &origin, PermissionType type, bool reply); @@ -193,30 +198,53 @@ public: void clearHttpCache(); + void setUseForGlobalCertificateVerification(bool enable = true); + bool isUsedForGlobalCertificateVerification() const; + + void addPageRequestInterceptor(); + void removePageRequestInterceptor(); + bool hasPageRequestInterceptor() const { return m_pageRequestInterceptors > 0; } + + QWebEngineClientCertificateStore *clientCertificateStore(); + + QHash<QByteArray, QWeakPointer<UserNotificationController>> &ephemeralNotifications() + { return m_ephemeralNotifications; } + QHash<QByteArray, QSharedPointer<UserNotificationController>> &persistentNotifications() + { return m_persistentNotifications; } + private: void updateCustomUrlSchemeHandlers(); void resetVisitedLinksManager(); + bool persistVisitedLinks() const; QString m_name; bool m_offTheRecord; + bool m_usedForGlobalCertificateVerification = false; QScopedPointer<ProfileQt> m_profile; QScopedPointer<VisitedLinksManagerQt> m_visitedLinksManager; QScopedPointer<DownloadManagerDelegateQt> m_downloadManagerDelegate; QScopedPointer<UserResourceControllerHost> m_userResourceController; QScopedPointer<QWebEngineCookieStore> m_cookieStore; + QWebEngineClientCertificateStore *m_clientCertificateStore = nullptr; QPointer<QWebEngineUrlRequestInterceptor> m_requestInterceptor; QString m_dataPath; + QString m_downloadPath; QString m_cachePath; QString m_httpUserAgent; HttpCacheType m_httpCacheType; QString m_httpAcceptLanguage; PersistentCookiesPolicy m_persistentCookiesPolicy; VisitedLinksPolicy m_visitedLinksPolicy; - QHash<QByteArray, QWebEngineUrlSchemeHandler *> m_customUrlSchemeHandlers; + QHash<QByteArray, QPointer<QWebEngineUrlSchemeHandler>> m_customUrlSchemeHandlers; + QHash<QByteArray, QWeakPointer<UserNotificationController>> m_ephemeralNotifications; + QHash<QByteArray, QSharedPointer<UserNotificationController>> m_persistentNotifications; + QList<ProfileAdapterClient*> m_clients; QVector<WebContentsAdapterClient *> m_webContentsAdapterClients; int m_httpCacheMaxSize; + int m_pageRequestInterceptors; + QrcUrlSchemeHandler m_qrcHandler; Q_DISABLE_COPY(ProfileAdapter) }; diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h index 19af12ca4..8ee9d240e 100644 --- a/src/core/profile_adapter_client.h +++ b/src/core/profile_adapter_client.h @@ -52,12 +52,14 @@ #define PROFILE_ADAPTER_CLIENT_H #include "qtwebenginecoreglobal_p.h" +#include <QSharedPointer> #include <QString> #include <QUrl> namespace QtWebEngineCore { class WebContentsAdapterClient; +class UserNotificationController; class QWEBENGINECORE_PRIVATE_EXPORT ProfileAdapterClient { @@ -142,6 +144,9 @@ public: virtual void downloadRequested(DownloadItemInfo &info) = 0; virtual void downloadUpdated(const DownloadItemInfo &info) = 0; + virtual void useForGlobalCertificateVerificationChanged() {} + virtual void showNotification(QSharedPointer<UserNotificationController> &) { } + virtual void addWebContentsAdapterClient(WebContentsAdapterClient *adapter) = 0; virtual void removeWebContentsAdapterClient(WebContentsAdapterClient *adapter) = 0; static QString downloadInterruptReasonToString(DownloadInterruptReason reason); diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 63585b2c3..f1fe96f81 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -39,15 +39,17 @@ #include "profile_io_data_qt.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "components/certificate_transparency/ct_known_logs.h" #include "components/network_session_configurator/common/network_features.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/cookie_store_factory.h" #include "content/public/common/content_features.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -76,16 +78,26 @@ #include "services/file/user_id_map.h" #include "services/network/proxy_service_mojo.h" +#include "net/client_cert_override.h" +#include "net/client_cert_store_data.h" #include "net/cookie_monster_delegate_qt.h" #include "net/custom_protocol_handler.h" #include "net/network_delegate_qt.h" #include "net/proxy_config_service_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" #include "profile_qt.h" #include "resource_context_qt.h" #include "type_conversion.h" +#if defined(USE_NSS_CERTS) +#include "net/cert_net/nss_ocsp.h" +#endif + +#if defined(OS_LINUX) || defined(OS_MACOSX) +#include "net/cert/cert_net_fetcher.h" +#include "net/cert_net/cert_net_fetcher_impl.h" +#endif + namespace QtWebEngineCore { static const char* const kDefaultAuthSchemes[] = { net::kBasicAuthScheme, @@ -102,8 +114,6 @@ static bool doNetworkSessionParamsMatch(const net::HttpNetworkSession::Params &f return false; if (first.enable_channel_id != second.enable_channel_id) return false; - if (first.enable_token_binding != second.enable_token_binding) - return false; return true; } @@ -153,13 +163,13 @@ static net::HttpNetworkSession::Params generateNetworkSessionParams(bool ignoreC { net::HttpNetworkSession::Params network_session_params; network_session_params.ignore_certificate_errors = ignoreCertificateErrors; - network_session_params.enable_token_binding = base::FeatureList::IsEnabled(features::kTokenBinding); network_session_params.enable_channel_id = base::FeatureList::IsEnabled(features::kChannelID); return network_session_params; } ProfileIODataQt::ProfileIODataQt(ProfileQt *profile) : m_profile(profile), + m_clientCertificateStoreData(new ClientCertificateStoreData), m_mutex(QMutex::Recursive), m_weakPtrFactory(this) { @@ -171,8 +181,19 @@ ProfileIODataQt::~ProfileIODataQt() { if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::IO)) DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(nullptr); +#endif +#if defined(OS_LINUX) ||defined(OS_MACOSX) + net::ShutdownGlobalCertNetFetcher(); +#endif + } + if (m_urlRequestContext && m_urlRequestContext->proxy_resolution_service()) m_urlRequestContext->proxy_resolution_service()->OnShutdown(); + m_resourceContext.reset(); if (m_cookieDelegate) m_cookieDelegate->setCookieMonster(0); // this will let CookieMonsterDelegateQt be deleted @@ -180,9 +201,16 @@ ProfileIODataQt::~ProfileIODataQt() delete m_proxyConfigService.fetchAndStoreAcquire(0); } +QPointer<ProfileAdapter> ProfileIODataQt::profileAdapter() +{ + return m_profileAdapter; +} + void ProfileIODataQt::shutdownOnUIThread() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + delete m_clientCertificateStoreData; + m_clientCertificateStoreData = nullptr; bool posted = content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE, this); if (!posted) { qWarning() << "Could not delete ProfileIODataQt on io thread !"; @@ -203,6 +231,13 @@ content::ResourceContext *ProfileIODataQt::resourceContext() return m_resourceContext.get(); } +#if BUILDFLAG(ENABLE_EXTENSIONS) +extensions::ExtensionSystemQt* ProfileIODataQt::GetExtensionSystem() +{ + return m_profile->GetExtensionSystem(); +} +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + void ProfileIODataQt::initializeOnIOThread() { m_networkDelegate.reset(new NetworkDelegateQt(this)); @@ -214,6 +249,7 @@ void ProfileIODataQt::initializeOnIOThread() QMutexLocker lock(&m_mutex); generateAllStorage(); generateJobFactory(); + setGlobalCertificateVerification(); m_initialized = true; } @@ -280,7 +316,15 @@ void ProfileIODataQt::generateStorage() net::ProxyConfigService *proxyConfigService = m_proxyConfigService.fetchAndStoreAcquire(0); Q_ASSERT(proxyConfigService); - m_storage->set_cert_verifier(net::CertVerifier::CreateDefault()); + std::unique_ptr<net::CertVerifier> cert_verifier = net::CertVerifier::CreateDefault(); + net::CertVerifier::Config config; + // Enable revocation checking: + config.enable_rev_checking = true; + // Mirroring Android WebView (we have no beef with Symantec, and our users might use them): + config.disable_symantec_enforcement = true; + cert_verifier->SetConfig(config); + + m_storage->set_cert_verifier(std::move(cert_verifier)); std::unique_ptr<net::MultiLogCTVerifier> ct_verifier(new net::MultiLogCTVerifier()); std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs; for (const auto &ct_log : certificate_transparency::GetKnownLogs()) { @@ -305,7 +349,7 @@ void ProfileIODataQt::generateStorage() scoped_refptr<base::SequencedTaskRunner> background_task_runner( base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), - base::TaskPriority::BACKGROUND, + base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::BLOCK_SHUTDOWN})); m_transportSecurityPersister = std::make_unique<net::TransportSecurityPersister>( @@ -346,7 +390,7 @@ void ProfileIODataQt::generateCookieStore() channel_id_db = new net::SQLiteChannelIDStore( toFilePath(m_channelIdPath), base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND})); + {base::MayBlock(), base::TaskPriority::BEST_EFFORT})); } m_storage->set_channel_id_service( @@ -361,8 +405,8 @@ void ProfileIODataQt::generateCookieStore() base::FilePath(), false, false, - nullptr) - ); + nullptr), + nullptr); break; case ProfileAdapter::AllowPersistentCookies: cookieStore = content::CreateCookieStore( @@ -370,8 +414,8 @@ void ProfileIODataQt::generateCookieStore() toFilePath(m_cookiesPath), false, true, - nullptr) - ); + nullptr), + nullptr); break; case ProfileAdapter::ForcePersistentCookies: cookieStore = content::CreateCookieStore( @@ -379,8 +423,8 @@ void ProfileIODataQt::generateCookieStore() toFilePath(m_cookiesPath), true, true, - nullptr) - ); + nullptr), + nullptr); break; } @@ -477,13 +521,10 @@ void ProfileIODataQt::generateJobFactory() std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>( new net::DataProtocolHandler())); scoped_refptr<base::TaskRunner> taskRunner(base::CreateTaskRunnerWithTraits({base::MayBlock(), - base::TaskPriority::BACKGROUND, + base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); jobFactory->SetProtocolHandler(url::kFileScheme, std::make_unique<net::FileProtocolHandler>(taskRunner)); - jobFactory->SetProtocolHandler(kQrcSchemeQt, - std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>( - new QrcProtocolHandlerQt())); jobFactory->SetProtocolHandler(url::kFtpScheme, net::FtpProtocolHandler::Create(m_urlRequestContext->host_resolver())); @@ -541,6 +582,21 @@ void ProfileIODataQt::regenerateJobFactory() } } +void ProfileIODataQt::setGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + QMutexLocker lock(&m_mutex); + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + // Set request context used by NSS for OCSP requests. + net::SetURLRequestContextForNSSHttpIO(m_urlRequestContext.get()); +#endif +#if defined(OS_LINUX) || defined(OS_MACOSX) + net::SetGlobalCertNetFetcher(net::CreateCertNetFetcher(m_urlRequestContext.get())); +#endif + } +} + void ProfileIODataQt::setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors) { @@ -563,6 +619,7 @@ void ProfileIODataQt::setFullConfiguration() m_httpCachePath = m_profileAdapter->httpCachePath(); m_httpCacheMaxSize = m_profileAdapter->httpCacheMaxSize(); m_customUrlSchemes = m_profileAdapter->customUrlSchemes(); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); m_dataPath = m_profileAdapter->dataPath(); } @@ -572,8 +629,8 @@ void ProfileIODataQt::requestStorageGeneration() { if (m_initialized && !m_updateAllStorage) { m_updateAllStorage = true; createProxyConfig(); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::generateAllStorage, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); } } @@ -586,10 +643,14 @@ void ProfileIODataQt::createProxyConfig() // must synchronously run on the glib message loop. This will be passed to // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). Q_ASSERT(m_proxyConfigService == 0); + net::ProxyConfigWithAnnotation initialConfig; + ProxyPrefs::ConfigState initialConfigState = PrefProxyConfigTrackerImpl::ReadPrefConfig( + m_profileAdapter->profile()->GetPrefs(), &initialConfig); m_proxyConfigService = new ProxyConfigServiceQt( net::ProxyResolutionService::CreateSystemProxyConfigService( - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO))); + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})), + initialConfig, initialConfigState); //pass interface to io thread m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithStrongBinding().PassInterface(); } @@ -656,8 +717,8 @@ void ProfileIODataQt::updateJobFactory() if (m_initialized && !m_updateJobFactory) { m_updateJobFactory = true; - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); } } @@ -666,6 +727,7 @@ void ProfileIODataQt::updateRequestInterceptor() Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); QMutexLocker lock(&m_mutex); m_requestInterceptor = m_profileAdapter->requestInterceptor(); + m_hasPageInterceptors = m_profileAdapter->hasPageRequestInterceptor(); // We in this case do not need to regenerate any Chromium classes. } @@ -675,6 +737,18 @@ QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() return m_requestInterceptor; } +QWebEngineUrlRequestInterceptor *ProfileIODataQt::requestInterceptor() +{ + return m_requestInterceptor; +} + +bool ProfileIODataQt::hasPageInterceptors() +{ + // used in NetworkDelegateQt::OnBeforeURLRequest + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return m_hasPageInterceptors; +} + void ProfileIODataQt::releaseInterceptor() { m_mutex.unlock(); @@ -690,4 +764,31 @@ bool ProfileIODataQt::canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) return m_cookieDelegate->canGetCookies(firstPartyUrl, url); } +void ProfileIODataQt::updateUsedForGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + QMutexLocker lock(&m_mutex); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); + + if (m_useForGlobalCertificateVerification) + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); +} + +ClientCertificateStoreData *ProfileIODataQt::clientCertificateStoreData() +{ + return m_clientCertificateStoreData; +} + +std::unique_ptr<net::ClientCertStore> ProfileIODataQt::CreateClientCertStore() +{ + return std::unique_ptr<net::ClientCertStore>(new ClientCertOverrideStore(m_clientCertificateStoreData)); +} + +// static +ProfileIODataQt *ProfileIODataQt::FromResourceContext(content::ResourceContext *resource_context) +{ + return static_cast<ResourceContextQt *>(resource_context)->m_io_data; +} + } // namespace QtWebEngineCore diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 2d4706bf4..edb2fd3b5 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" +#include "extensions/buildflags/buildflags.h" #include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" #include <QtCore/QString> @@ -50,6 +51,7 @@ #include <QtCore/QMutex> namespace net { +class ClientCertStore; class DhcpPacFileFetcherFactory; class HttpAuthPreferences; class HttpNetworkSession; @@ -61,8 +63,13 @@ class URLRequestJobFactoryImpl; class TransportSecurityPersister; } +namespace extensions { +class ExtensionSystemQt; +} + namespace QtWebEngineCore { +class ClientCertificateStoreData; class ProfileQt; // ProfileIOData contains data that lives on the IOthread @@ -75,8 +82,13 @@ public: ProfileIODataQt(ProfileQt *profile); // runs on ui thread virtual ~ProfileIODataQt(); + QPointer<ProfileAdapter> profileAdapter(); content::ResourceContext *resourceContext(); net::URLRequestContext *urlRequestContext(); +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionSystemQt* GetExtensionSystem(); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + void initializeOnIOThread(); void initializeOnUIThread(); // runs on ui thread void shutdownOnUIThread(); // runs on ui thread @@ -91,10 +103,12 @@ public: void regenerateJobFactory(); bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const; bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const; + void setGlobalCertificateVerification(); // Used in NetworkDelegateQt::OnBeforeURLRequest. QWebEngineUrlRequestInterceptor *acquireInterceptor(); void releaseInterceptor(); + QWebEngineUrlRequestInterceptor *requestInterceptor(); void setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors); @@ -107,7 +121,12 @@ public: void updateRequestInterceptor(); // runs on ui thread void requestStorageGeneration(); //runs on ui thread void createProxyConfig(); //runs on ui thread + void updateUsedForGlobalCertificateVerification(); // runs on ui thread + bool hasPageInterceptors(); + ClientCertificateStoreData *clientCertificateStoreData(); + std::unique_ptr<net::ClientCertStore> CreateClientCertStore(); + static ProfileIODataQt *FromResourceContext(content::ResourceContext *resource_context); private: ProfileQt *m_profile; std::unique_ptr<net::URLRequestContextStorage> m_storage; @@ -129,6 +148,7 @@ private: QAtomicPointer<net::ProxyConfigService> m_proxyConfigService; QPointer<ProfileAdapter> m_profileAdapter; // never dereferenced in IO thread and it is passed by qpointer ProfileAdapter::PersistentCookiesPolicy m_persistentCookiesPolicy; + ClientCertificateStoreData *m_clientCertificateStoreData; QString m_cookiesPath; QString m_channelIdPath; QString m_httpAcceptLanguage; @@ -144,6 +164,8 @@ private: bool m_updateAllStorage = false; bool m_updateJobFactory = false; bool m_ignoreCertificateErrors = false; + bool m_useForGlobalCertificateVerification = false; + bool m_hasPageInterceptors = false; base::WeakPtrFactory<ProfileIODataQt> m_weakPtrFactory; // this should be always the last member QString m_dataPath; DISALLOW_COPY_AND_ASSIGN(ProfileIODataQt); diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp index df05d891e..39500629a 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -41,6 +41,7 @@ #include "profile_adapter.h" #include "browsing_data_remover_delegate_qt.h" +#include "command_line_pref_store_qt.h" #include "download_manager_delegate_qt.h" #include "net/ssl_host_state_delegate_qt.h" #include "net/url_request_context_getter_qt.h" @@ -48,6 +49,7 @@ #include "qtwebenginecoreglobal_p.h" #include "type_conversion.h" #include "web_engine_library_info.h" +#include "web_engine_context.h" #include "base/time/time.h" #include "content/public/browser/browser_thread.h" @@ -62,22 +64,38 @@ #include "components/prefs/pref_service_factory.h" #include "components/prefs/pref_registry_simple.h" #include "components/user_prefs/user_prefs.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" +#include "chrome/common/pref_names.h" #if QT_CONFIG(webengine_spellchecker) #include "chrome/browser/spellchecker/spellcheck_service.h" -#include "chrome/common/pref_names.h" #include "components/spellcheck/browser/pref_names.h" #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "components/guest_view/browser/guest_view_manager.h" +#include "extensions/browser/extension_protocols.h" +#include "extensions/browser/pref_names.h" +#include "extensions/browser/process_manager.h" +#include "extensions/common/constants.h" + +#include "extensions/extension_system_qt.h" +#endif + namespace QtWebEngineCore { ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) : m_profileIOData(new ProfileIODataQt(this)), m_profileAdapter(profileAdapter) +#if BUILDFLAG(ENABLE_EXTENSIONS) + , m_extensionSystem(nullptr) +#endif // BUILDFLAG(ENABLE_EXTENSIONS) { PrefServiceFactory factory; factory.set_user_prefs(new InMemoryPrefStore); + factory.set_command_line_prefs(base::MakeRefCounted<CommandLinePrefStoreQt>( + WebEngineContext::commandLine())); PrefRegistrySimple *registry = new PrefRegistrySimple(); - + PrefProxyConfigTrackerImpl::RegisterPrefs(registry); #if QT_CONFIG(webengine_spellchecker) // Initial spellcheck settings registry->RegisterStringPref(prefs::kAcceptLanguages, std::string()); @@ -87,6 +105,26 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) registry->RegisterBooleanPref(spellcheck::prefs::kSpellCheckEnable, false); registry->RegisterBooleanPref(spellcheck::prefs::kSpellCheckUseSpellingService, false); #endif // QT_CONFIG(webengine_spellchecker) + registry->RegisterBooleanPref(prefs::kShowInternalAccessibilityTree, false); + registry->RegisterIntegerPref(prefs::kNotificationNextPersistentId, 10000); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + registry->RegisterDictionaryPref(extensions::pref_names::kExtensions); + registry->RegisterListPref(extensions::pref_names::kInstallAllowList); + registry->RegisterListPref(extensions::pref_names::kInstallDenyList); + registry->RegisterDictionaryPref(extensions::pref_names::kInstallForceList); + registry->RegisterDictionaryPref(extensions::pref_names::kInstallLoginScreenAppList); + registry->RegisterListPref(extensions::pref_names::kAllowedTypes); + registry->RegisterBooleanPref(extensions::pref_names::kStorageGarbageCollect, false); + registry->RegisterInt64Pref(extensions::pref_names::kLastUpdateCheck, 0); + registry->RegisterInt64Pref(extensions::pref_names::kNextUpdateCheck, 0); + registry->RegisterListPref(extensions::pref_names::kAllowedInstallSites); + registry->RegisterStringPref(extensions::pref_names::kLastChromeVersion, std::string()); + registry->RegisterListPref(extensions::pref_names::kNativeMessagingBlacklist); + registry->RegisterListPref(extensions::pref_names::kNativeMessagingWhitelist); + registry->RegisterBooleanPref(extensions::pref_names::kNativeMessagingUserLevelHosts, true); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + m_prefService = factory.Create(registry); user_prefs::UserPrefs::Set(this, m_prefService.get()); @@ -95,6 +133,11 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) // ProfileQt object is allocated at the same address as a previously // destroyed one. Needs to be called after WebEngineContext initialization. BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + m_extensionSystem = static_cast<extensions::ExtensionSystemQt*>(extensions::ExtensionSystem::Get(this)); + m_extensionSystem->InitForRegularProfile(true); +#endif } ProfileQt::~ProfileQt() @@ -123,6 +166,11 @@ base::FilePath ProfileQt::GetPath() const return toFilePath(m_profileAdapter->dataPath()); } +base::FilePath ProfileQt::GetCachePath() const +{ + return toFilePath(m_profileAdapter->cachePath()); +} + bool ProfileQt::IsOffTheRecord() const { return m_profileAdapter->isOffTheRecord(); @@ -156,7 +204,11 @@ content::DownloadManagerDelegate *ProfileQt::GetDownloadManagerDelegate() content::BrowserPluginGuestManager *ProfileQt::GetGuestManager() { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return guest_view::GuestViewManager::FromBrowserContext(this); +#else return nullptr; +#endif } storage::SpecialStoragePolicy *ProfileQt::GetSpecialStoragePolicy() @@ -210,6 +262,12 @@ net::URLRequestContextGetter *ProfileQt::CreateRequestContext( { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!m_urlRequestContextGetter.get()); +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::InfoMap* extension_info_map = GetExtensionSystem()->info_map(); + (*protocol_handlers)[extensions::kExtensionScheme] = + extensions::CreateExtensionProtocolHandler(IsOffTheRecord(),extension_info_map); +#endif + m_profileIOData->setRequestContextData(protocol_handlers, std::move(request_interceptors)); m_profileIOData->updateStorageSettings(); m_urlRequestContextGetter = new URLRequestContextGetterQt(m_profileIOData.get()); @@ -229,8 +287,8 @@ net::URLRequestContextGetter *ProfileQt::CreateRequestContextForStoragePartition void ProfileQt::FailedToLoadDictionary(const std::string &language) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - qWarning() << "Could not load dictionary for:" << toQt(language) << endl - << "Make sure that correct bdic file is in:" << toQt(WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES).value()); + LOG(WARNING) << "Could not load dictionary for:" << language; + LOG(INFO) << "Make sure that correct bdic file is in:" << WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES); } void ProfileQt::setSpellCheckLanguages(const QStringList &languages) @@ -266,4 +324,12 @@ bool ProfileQt::isSpellCheckEnabled() const return m_prefService->GetBoolean(spellcheck::prefs::kSpellCheckEnable); } #endif // QT_CONFIG(webengine_spellchecker) + +#if BUILDFLAG(ENABLE_EXTENSIONS) +extensions::ExtensionSystemQt* ProfileQt::GetExtensionSystem() +{ + return m_extensionSystem; +} +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + } // namespace QtWebEngineCore diff --git a/src/core/profile_qt.h b/src/core/profile_qt.h index 00119c053..41eeeecf5 100644 --- a/src/core/profile_qt.h +++ b/src/core/profile_qt.h @@ -43,6 +43,7 @@ #include "chrome/browser/profiles/profile.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/resource_context.h" +#include "extensions/buildflags/buildflags.h" #include "net/url_request/url_request_context.h" #include "profile_io_data_qt.h" #include <QtGlobal> @@ -53,6 +54,10 @@ QT_END_NAMESPACE class InMemoryPrefStore; class PrefService; +namespace extensions { +class ExtensionSystemQt; +} + namespace QtWebEngineCore { class ProfileAdapter; @@ -68,6 +73,7 @@ public: // BrowserContext implementation: base::FilePath GetPath() const override; + base::FilePath GetCachePath() const override; bool IsOffTheRecord() const override; net::URLRequestContextGetter *CreateMediaRequestContext() override; @@ -98,6 +104,7 @@ public: PrefService *GetPrefs() override; const PrefService *GetPrefs() const override; net::URLRequestContextGetter *GetRequestContext() override; + void Initialize(); ProfileAdapter *profileAdapter() { return m_profileAdapter; } @@ -108,6 +115,9 @@ public: void setSpellCheckEnabled(bool enabled); bool isSpellCheckEnabled() const; #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionSystemQt* GetExtensionSystem(); +#endif // defined(ENABLE_EXTENSIONS) private: friend class ContentBrowserClientQt; @@ -119,6 +129,10 @@ private: std::unique_ptr<ProfileIODataQt> m_profileIOData; ProfileAdapter *m_profileAdapter; friend class ProfileAdapter; +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionSystemQt *m_extensionSystem; +#endif //ENABLE_EXTENSIONS + friend class BrowserContextAdapter; DISALLOW_COPY_AND_ASSIGN(ProfileQt); }; diff --git a/src/core/qtwebengine.gni b/src/core/qtwebengine.gni index 14da1e6cf..7c0ca763e 100644 --- a/src/core/qtwebengine.gni +++ b/src/core/qtwebengine.gni @@ -1,5 +1,6 @@ import("//build/config/ui.gni") import("//media/media_options.gni") +import("//extensions/buildflags/buildflags.gni") import("//third_party/widevine/cdm/widevine.gni") chromium_version = exec_script("//build/util/version.py", [ "-f", rebase_path("//chrome/VERSION"), @@ -9,6 +10,7 @@ chromium_version = exec_script("//build/util/version.py", [ "-f", rebase_path("/ include_dirs = [ "//skia/config", "//third_party", + "//third_party/boringssl/src/include", "//third_party/skia/include/core" ] @@ -25,20 +27,19 @@ deps = [ "//components/web_cache/browser", "//components/web_cache/renderer", "//components/spellcheck:buildflags", + "//components/proxy_config", "//content/public/app:browser", - "//content/public/browser", - "//content/public/common", - "//content/public/renderer", + "//content", "//media:media_buildflags", "//net:net_with_v8", "//services/proxy_resolver:lib", "//skia", "//third_party/blink/public:blink", - "//third_party/mesa:mesa_headers", "//ui/accessibility", + "//ui/gl", "//qtwebengine/browser:interfaces", ":qtwebengine_sources", - ":qtwebengine_resources" + ":qtwebengine_resources", ] if (enable_webrtc) { @@ -49,12 +50,22 @@ if (is_linux && !is_desktop_linux) { deps += [ "//ui/events/ozone:events_ozone_evdev"] } +if (use_xscrnsaver) { + deps += [ "//ui/base/x" ] +} + if (use_ozone) { deps += [ "//ui/ozone/common" ] } +if (enable_extensions) { + deps += [ + ":qtwebengine_extensions_features" + ] +} + data_deps = [ "//qtwebengine/browser:service_manifests" ] defines = [ diff --git a/src/core/qtwebengine_resources.gni b/src/core/qtwebengine_resources.gni index 6e8c3c6eb..283235f66 100644 --- a/src/core/qtwebengine_resources.gni +++ b/src/core/qtwebengine_resources.gni @@ -1,10 +1,14 @@ import("//tools/grit/repack.gni") import("//build/config/locales.gni") import("//chrome/chrome_repack_locales.gni") +import("//extensions/buildflags/buildflags.gni") group("qtwebengine_resources") { deps = [ "//chrome/app:generated_resources", + "//chrome/browser:resources", + "//chrome/browser/resources:component_extension_resources", + "//chrome/common:resources", "//components/resources:components_resources", ":qtwebengine_repack_resources", ":qtwebengine_repack_resources_100", @@ -17,6 +21,8 @@ group("qtwebengine_resources") { repack("qtwebengine_repack_resources") { sources = [ "$root_gen_dir/qtwebengine/qt_webengine_resources.pak", + "$root_gen_dir/chrome/browser_resources.pak", + "$root_gen_dir/chrome/common_resources.pak", "$root_gen_dir/chrome/quota_internals_resources.pak", "$root_gen_dir/chrome/task_scheduler_internals_resources.pak", "$root_gen_dir/components/components_resources.pak", @@ -31,6 +37,8 @@ repack("qtwebengine_repack_resources") { "//qtwebengine/browser:qt_webengine_resources", "//chrome/browser/resources:quota_internals_resources", "//chrome/browser/resources:task_scheduler_internals_resources", + "//chrome/browser:resources_grit", + "//chrome/common:resources_grit", "//components/resources:components_resources_grit", "//content:resources_grit", "//mojo/public/js:resources", @@ -38,6 +46,20 @@ repack("qtwebengine_repack_resources") { "//third_party/blink/public:resources_grit", "//ui/resources:webui_resources_grd_grit", ] + + if (enable_extensions) { + sources += [ + "$root_gen_dir/chrome/component_extension_resources.pak", + "$root_gen_dir/extensions/extensions_renderer_resources.pak", + "$root_gen_dir/extensions/extensions_resources.pak", + ] + deps += [ + "//chrome/browser/resources:component_extension_resources_grit", + "//extensions:extensions_renderer_resources_grit", + "//extensions:extensions_resources_grd_grit", + ] + } + } repack("qtwebengine_repack_resources_100") { @@ -56,6 +78,14 @@ repack("qtwebengine_repack_resources_100") { "//third_party/blink/public:scaled_resources_100_percent", "//ui/resources:ui_resources_grd_grit" ] + if (enable_extensions) { + sources += [ + "$root_gen_dir/extensions/extensions_browser_resources_100_percent.pak" + ] + deps += [ + "//extensions:extensions_browser_resources_grit" + ] + } } repack("qtwebengine_repack_resources_200") { @@ -74,6 +104,14 @@ repack("qtwebengine_repack_resources_200") { "//third_party/blink/public:scaled_resources_200_percent", "//ui/resources:ui_resources_grd_grit" ] + if (enable_extensions) { + sources += [ + "$root_gen_dir/extensions/extensions_browser_resources_200_percent.pak" + ] + deps += [ + "//extensions:extensions_browser_resources_grit" + ] + } } repack("qtwebengine_repack_resources_devtools") { diff --git a/src/core/qtwebengine_sources.gni b/src/core/qtwebengine_sources.gni index b1361e727..58df7096b 100644 --- a/src/core/qtwebengine_sources.gni +++ b/src/core/qtwebengine_sources.gni @@ -1,9 +1,11 @@ import("//build/config/features.gni") import("//build/config/ui.gni") +import("//chrome/common/features.gni") import("//components/spellcheck/spellcheck_build_features.gni") import("//pdf/features.gni") import("//ppapi/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni") +import("//extensions/buildflags/buildflags.gni") source_set("qtwebengine_spellcheck_sources") { include_dirs = core_include_dirs @@ -40,22 +42,22 @@ source_set("qtwebengine_sources") { "//skia:skia_config", "//third_party/boringssl:external_config", ] + deps = [ "//chrome/common:buildflags", "//components/nacl/common:buildflags", "//extensions/buildflags:buildflags", "//third_party/blink/public/mojom:mojom_platform", ] + sources = [ - "//chrome/common/custom_handlers/protocol_handler.cc", - "//chrome/common/custom_handlers/protocol_handler.h", + "//chrome/browser/accessibility/accessibility_ui.cc", + "//chrome/browser/accessibility/accessibility_ui.h", "//chrome/browser/custom_handlers/protocol_handler_registry.cc", "//chrome/browser/custom_handlers/protocol_handler_registry.h", "//chrome/browser/custom_handlers/protocol_handler_registry_factory.cc", "//chrome/browser/custom_handlers/protocol_handler_registry_factory.h", "//chrome/browser/media/webrtc/desktop_media_list.h", - "//chrome/browser/media/webrtc/desktop_streams_registry.cc", - "//chrome/browser/media/webrtc/desktop_streams_registry.h", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h", "//chrome/browser/profiles/profile.cc", @@ -72,6 +74,8 @@ source_set("qtwebengine_sources") { "//chrome/browser/ui/webui/quota_internals/quota_internals_ui.h", "//chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc", "//chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h", + "//chrome/common/custom_handlers/protocol_handler.cc", + "//chrome/common/custom_handlers/protocol_handler.h", "//chrome/common/chrome_switches.cc", "//chrome/common/chrome_switches.h", "//chrome/common/pref_names.cc", @@ -80,12 +84,47 @@ source_set("qtwebengine_sources") { "//chrome/common/url_constants.h", "//chrome/common/webui_url_constants.cc", "//chrome/common/webui_url_constants.h", - "//extensions/common/constants.cc", - "//extensions/common/constants.h", - "//extensions/common/url_pattern.cc", - "//extensions/common/url_pattern.h", + "//components/prefs/in_memory_pref_store.cc", + "//components/prefs/in_memory_pref_store.h", ] + if (enable_extensions) { + deps += [ + ":qtwebengine_extensions_features", + "//chrome/browser/resources:component_extension_resources_grit", + "//chrome/common/extensions/api", + "//chrome/common/extensions/api:extensions_features", + "//components/crx_file", + "//components/crx_file:crx_creator", + "//components/spellcheck:buildflags", + "//extensions/buildflags:buildflags", + "//extensions/common", + "//extensions/common/api", + "//extensions/common:core_api_provider", + "//extensions/browser", + "//extensions/browser/api", + "//extensions/browser:core_api_provider", + "//extensions/renderer", + "//extensions:extensions_resources", + "//extensions/strings", + ] + sources += [ + "//chrome/common/extensions/permissions/chrome_api_permissions.cc", + "//chrome/common/extensions/permissions/chrome_api_permissions.h", + "//chrome/common/extensions/permissions/chrome_permission_message_provider.cc", + "//chrome/common/extensions/permissions/chrome_permission_message_provider.h", + "//chrome/common/extensions/permissions/chrome_permission_message_rules.cc", + "//chrome/common/extensions/permissions/chrome_permission_message_rules.h", + ] + } else { + sources += [ + "//extensions/common/constants.cc", + "//extensions/common/constants.h", + "//extensions/common/url_pattern.cc", + "//extensions/common/url_pattern.h", + ] + } + if (is_linux) { sources += [ "//chrome/browser/ui/webui/sandbox_internals_ui.cc", @@ -109,11 +148,6 @@ source_set("qtwebengine_sources") { "//chrome/renderer/pepper/pepper_shared_memory_message_filter.cc", "//chrome/renderer/pepper/pepper_shared_memory_message_filter.h", ] - - deps += [ - # Need to depend on //content/ppapi_plugin, which is private, thus depending on parent. - "//content", - ] } if (enable_basic_printing || enable_print_preview) { @@ -136,6 +170,8 @@ source_set("qtwebengine_sources") { deps += [ "//pdf", "//pdf:buildflags", + "//components/pdf/browser:browser", + "//components/pdf/renderer:renderer", "//components/printing/browser", "//components/printing/renderer", ] diff --git a/src/core/quota_permission_context_qt.cpp b/src/core/quota_permission_context_qt.cpp index cb1467364..a502e7fc8 100644 --- a/src/core/quota_permission_context_qt.cpp +++ b/src/core/quota_permission_context_qt.cpp @@ -39,7 +39,9 @@ #include "quota_permission_context_qt.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "quota_request_controller_impl.h" @@ -64,10 +66,10 @@ void QuotaPermissionContextQt::RequestQuotaPermission(const StorageQuotaParams & } if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::Bind(&QuotaPermissionContextQt::RequestQuotaPermission, this, - params, render_process_id, callback)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&QuotaPermissionContextQt::RequestQuotaPermission, this, + params, render_process_id, callback)); return; } @@ -95,10 +97,10 @@ void QuotaPermissionContextQt::dispatchCallbackOnIOThread(const PermissionCallba return; if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&QuotaPermissionContextQt::dispatchCallbackOnIOThread, - this, callback, response)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&QuotaPermissionContextQt::dispatchCallbackOnIOThread, + this, callback, response)); return; } diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 40ed75973..9791c8c46 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -40,18 +40,20 @@ #include "render_widget_host_view_qt.h" #include "browser_accessibility_manager_qt.h" -#include "chromium_overrides.h" -#include "compositor.h" +#include "compositor/compositor.h" #include "qtwebenginecoreglobal_p.h" #include "render_widget_host_view_qt_delegate.h" +#include "touch_handle_drawable_client.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_event_factory.h" #include "components/viz/common/surfaces/frame_sink_id_allocator.h" -#include "content/browser/accessibility/browser_accessibility_state_impl.h" -#include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/frame_host/frame_tree.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/content_switches_internal.h" #include "content/browser/renderer_host/render_widget_host_input_event_router.h" @@ -61,9 +63,11 @@ #include "third_party/blink/public/platform/web_cursor_info.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/event.h" +#include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/gesture_detection/gesture_provider_config_helper.h" #include "ui/events/gesture_detection/motion_event.h" -#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/touch_selection/touch_selection_controller.h" #if defined(USE_OZONE) #include "ui/base/clipboard/scoped_clipboard_writer.h" @@ -82,7 +86,6 @@ #include <QFocusEvent> #include <QGuiApplication> #include <QInputMethodEvent> -#include <QLoggingCategory> #include <QTextFormat> #include <QKeyEvent> #include <QMouseEvent> @@ -93,7 +96,6 @@ #include <QWheelEvent> #include <QWindow> #include <QtGui/private/qinputcontrol_p.h> -#include <QtGui/qaccessible.h> namespace QtWebEngineCore { @@ -184,14 +186,13 @@ static inline bool isCommonTextEditShortcut(const QKeyEvent *ke) static uint32_t s_eventId = 0; class MotionEventQt : public ui::MotionEvent { public: - MotionEventQt(const QList<QTouchEvent::TouchPoint> &touchPoints, const base::TimeTicks &eventTime, Action action, const Qt::KeyboardModifiers modifiers, float dpiScale, int index = -1) + MotionEventQt(const QList<QTouchEvent::TouchPoint> &touchPoints, const base::TimeTicks &eventTime, Action action, const Qt::KeyboardModifiers modifiers, int index = -1) : touchPoints(touchPoints) , eventTime(eventTime) , action(action) , eventId(++s_eventId) , flags(flagsFromModifiers(modifiers)) , index(index) - , dpiScale(dpiScale) { // ACTION_DOWN and ACTION_UP must be accesssed through pointer_index 0 Q_ASSERT((action != Action::DOWN && action != Action::UP) || index == 0); @@ -202,8 +203,8 @@ public: int GetActionIndex() const override { return index; } size_t GetPointerCount() const override { return touchPoints.size(); } int GetPointerId(size_t pointer_index) const override { return touchPoints.at(pointer_index).id(); } - float GetX(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().x() / dpiScale; } - float GetY(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().y() / dpiScale; } + float GetX(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().x(); } + float GetY(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().y(); } float GetRawX(size_t pointer_index) const override { return touchPoints.at(pointer_index).screenPos().x(); } float GetRawY(size_t pointer_index) const override { return touchPoints.at(pointer_index).screenPos().y(); } float GetTouchMajor(size_t pointer_index) const override @@ -224,6 +225,8 @@ public: float GetPressure(size_t pointer_index) const override { return touchPoints.at(pointer_index).pressure(); } float GetTiltX(size_t pointer_index) const override { return 0; } float GetTiltY(size_t pointer_index) const override { return 0; } + float GetTwist(size_t) const override { return 0; } + float GetTangentialPressure(size_t) const override { return 0; } base::TimeTicks GetEventTime() const override { return eventTime; } size_t GetHistorySize() const override { return 0; } @@ -241,20 +244,22 @@ private: const uint32_t eventId; int flags; int index; - float dpiScale; }; -bool isAccessibilityEnabled() { - // On Linux accessibility is disabled by default due to performance issues, - // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment - // variable. For details, see QTBUG-59922. -#ifdef Q_OS_LINUX - static bool accessibility_enabled - = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); -#else - const bool accessibility_enabled = true; -#endif - return accessibility_enabled; +static content::ScreenInfo screenInfoFromQScreen(QScreen *screen) +{ + content::ScreenInfo r; + if (screen) { + r.device_scale_factor = screen->devicePixelRatio(); + r.depth_per_component = 8; + r.depth = screen->depth(); + r.is_monochrome = (r.depth == 1); + r.rect = toGfx(screen->geometry()); + r.available_rect = toGfx(screen->availableGeometry()); + } else { + r.device_scale_factor = qGuiApp->devicePixelRatio(); + } + return r; } RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget) @@ -262,7 +267,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget , m_gestureProvider(QtGestureProviderConfig(), this) , m_sendMotionActionDown(false) , m_touchMotionStarted(false) - , m_compositor(new Compositor(this)) + , m_compositor(new Compositor(widget)) , m_loadVisuallyCommittedState(NotCommitted) , m_adapterClient(0) , m_imeInProgress(false) @@ -273,20 +278,12 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget , m_cursorPosition(0) , m_emptyPreviousSelection(true) , m_wheelAckPending(false) - , m_pendingResize(false) , m_mouseWheelPhaseHandler(this) // This frame-sink id is based on what RenderWidgetHostViewChildFrame does: , m_frameSinkId(base::checked_cast<uint32_t>(widget->GetProcess()->GetID()), base::checked_cast<uint32_t>(widget->GetRoutingID())) { host()->SetView(this); -#ifndef QT_NO_ACCESSIBILITY - if (isAccessibilityEnabled()) { - QAccessible::installActivationObserver(this); - if (QAccessible::isActive()) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - } -#endif // QT_NO_ACCESSIBILITY if (GetTextInputManager()) GetTextInputManager()->AddObserver(this); @@ -294,27 +291,32 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget const QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); m_imeHasHiddenTextCapability = context && context->hasCapability(QPlatformInputContext::HiddenTextCapability); - m_localSurfaceId = m_localSurfaceIdAllocator.GenerateId(); - if (host()->delegate() && host()->delegate()->GetInputEventRouter()) host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(GetFrameSinkId(), this); + + m_touchSelectionControllerClient.reset(new TouchSelectionControllerClientQt(this)); + ui::TouchSelectionController::Config config; + config.max_tap_duration = base::TimeDelta::FromMilliseconds(ui::GestureConfiguration::GetInstance()->long_press_time_in_ms()); + config.tap_slop = ui::GestureConfiguration::GetInstance()->max_touch_move_in_pixels_for_click(); + config.enable_longpress_drag_selection = false; + m_touchSelectionController.reset(new ui::TouchSelectionController(m_touchSelectionControllerClient.get(), config)); } RenderWidgetHostViewQt::~RenderWidgetHostViewQt() { QObject::disconnect(m_adapterClientDestroyedConnection); -#ifndef QT_NO_ACCESSIBILITY - QAccessible::removeActivationObserver(this); -#endif // QT_NO_ACCESSIBILITY if (text_input_manager_) text_input_manager_->RemoveObserver(this); + + m_touchSelectionController.reset(); + m_touchSelectionControllerClient.reset(); } void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); - m_compositor->setViewDelegate(delegate); + visualPropertiesChanged(); } void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient) @@ -341,30 +343,16 @@ void RenderWidgetHostViewQt::InitAsFullscreen(content::RenderWidgetHostView*) { } -void RenderWidgetHostViewQt::SetSize(const gfx::Size& size) +void RenderWidgetHostViewQt::SetSize(const gfx::Size &sizeInDips) { - int width = size.width(); - int height = size.height(); - - m_delegate->resize(width,height); + m_delegate->resize(sizeInDips.width(), sizeInDips.height()); } -void RenderWidgetHostViewQt::SetBounds(const gfx::Rect& screenRect) +void RenderWidgetHostViewQt::SetBounds(const gfx::Rect &windowRectInDips) { - // This is called when webkit has sent us a Move message. - if (IsPopup()) - m_delegate->move(toQt(screenRect.origin())); - SetSize(screenRect.size()); -} - -gfx::Size RenderWidgetHostViewQt::GetCompositorViewportPixelSize() const -{ - if (!m_delegate || !m_delegate->window() || !m_delegate->window()->screen()) - return gfx::Size(); - - const QScreen* screen = m_delegate->window()->screen(); - gfx::SizeF size = toGfx(m_delegate->screenRect().size()); - return gfx::ToCeiledSize(gfx::ScaleSize(size, screen->devicePixelRatio())); + DCHECK(IsPopup()); + m_delegate->move(toQt(windowRectInDips.origin())); + m_delegate->resize(windowRectInDips.width(), windowRectInDips.height()); } gfx::NativeView RenderWidgetHostViewQt::GetNativeView() const @@ -443,11 +431,7 @@ bool RenderWidgetHostViewQt::IsShowing() // Retrieve the bounds of the View, in screen coordinates. gfx::Rect RenderWidgetHostViewQt::GetViewBounds() const { - QRectF p = m_delegate->contentsRect(); - float s = dpiScale(); - gfx::Point p1(floor(p.x() / s), floor(p.y() / s)); - gfx::Point p2(ceil(p.right() /s), ceil(p.bottom() / s)); - return gfx::BoundingRect(p1, p2); + return m_viewRectInDips; } void RenderWidgetHostViewQt::UpdateBackgroundColor() @@ -601,15 +585,13 @@ void RenderWidgetHostViewQt::DisplayCursor(const content::WebCursor &webCursor) } #if defined(USE_AURA) if (auraType != ui::CursorType::kNull) { - QWindow *window = m_delegate->window(); - qreal windowDpr = window ? window->devicePixelRatio() : 1.0f; int resourceId; gfx::Point hotspot; // GetCursorDataFor only knows hotspots for 1x and 2x cursor images, in physical pixels. - qreal hotspotDpr = windowDpr <= 1.0f ? 1.0f : 2.0f; + qreal hotspotDpr = m_screenInfo.device_scale_factor <= 1.0f ? 1.0f : 2.0f; if (ui::GetCursorDataFor(ui::CursorSize::kNormal, auraType, hotspotDpr, &resourceId, &hotspot)) { if (const gfx::ImageSkia *imageSkia = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resourceId)) { - QImage imageQt = toQImage(imageSkia->GetRepresentation(windowDpr)); + QImage imageQt = toQImage(imageSkia->GetRepresentation(m_screenInfo.device_scale_factor)); // Convert hotspot coordinates into device-independent pixels. qreal hotX = hotspot.x() / hotspotDpr; @@ -682,21 +664,18 @@ void RenderWidgetHostViewQt::DidCreateNewRendererCompositorFrameSink(viz::mojom: void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &local_surface_id, viz::CompositorFrame frame, base::Optional<viz::HitTestRegionList>) { bool scrollOffsetChanged = (m_lastScrollOffset != frame.metadata.root_scroll_offset); - bool contentsSizeChanged = (m_lastContentsSize != frame.metadata.root_layer_size); + bool contentsSizeChanged = (m_lastContentsSize != frame.metadata.scrollable_viewport_size); m_lastScrollOffset = frame.metadata.root_scroll_offset; - m_lastContentsSize = frame.metadata.root_layer_size; + m_lastContentsSize = frame.metadata.scrollable_viewport_size; // Force to process swap messages uint32_t frame_token = frame.metadata.frame_token; if (frame_token) OnFrameTokenChangedForView(frame_token); - // Support experimental.viewport.devicePixelRatio, see GetScreenInfo implementation below. - float dpiScale = this->dpiScale(); - if (dpiScale != 0 && dpiScale != 1) - frame.metadata.device_scale_factor /= dpiScale; - - m_compositor->submitFrame(std::move(frame)); + m_compositor->submitFrame( + std::move(frame), + base::BindOnce(&RenderWidgetHostViewQtDelegate::update, base::Unretained(m_delegate.get()))); if (m_loadVisuallyCommittedState == NotCommitted) { m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; @@ -709,31 +688,16 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo m_adapterClient->updateScrollPosition(toQt(m_lastScrollOffset)); if (contentsSizeChanged) m_adapterClient->updateContentsSize(toQt(m_lastContentsSize)); - - if (m_pendingResize && host()) { - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; - } } void RenderWidgetHostViewQt::GetScreenInfo(content::ScreenInfo *results) const { - QWindow *window = m_delegate->window(); - if (!window) - return; - GetScreenInfoFromNativeWindow(window, results); - - // Support experimental.viewport.devicePixelRatio - results->device_scale_factor *= dpiScale(); + *results = m_screenInfo; } gfx::Rect RenderWidgetHostViewQt::GetBoundsInRootWindow() { - if (!m_delegate->window()) - return gfx::Rect(); - - QRect r = m_delegate->window()->frameGeometry(); - return gfx::Rect(r.x(), r.y(), r.width(), r.height()); + return m_windowRectInDips; } void RenderWidgetHostViewQt::ClearCompositorFrame() @@ -902,7 +866,33 @@ void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture) return; } - host()->ForwardGestureEvent(ui::CreateWebGestureEventFromGestureEventData(gesture)); + blink::WebGestureEvent event = ui::CreateWebGestureEventFromGestureEventData(gesture); + + if (m_touchSelectionController && m_touchSelectionControllerClient) { + switch (event.GetType()) { + case blink::WebInputEvent::kGestureLongPress: + m_touchSelectionController->HandleLongPressEvent(event.TimeStamp(), event.PositionInWidget()); + break; + case blink::WebInputEvent::kGestureTap: + m_touchSelectionController->HandleTapEvent(event.PositionInWidget(), event.data.tap.tap_count); + break; + case blink::WebInputEvent::kGestureScrollBegin: + m_touchSelectionControllerClient->onScrollBegin(); + break; + case blink::WebInputEvent::kGestureScrollEnd: + m_touchSelectionControllerClient->onScrollEnd(); + break; + default: + break; + } + } + + host()->ForwardGestureEvent(event); +} + +void RenderWidgetHostViewQt::DidStopFlinging() +{ + m_touchSelectionControllerClient->DidStopFlinging(); } viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) @@ -915,27 +905,12 @@ viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties( void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata) { - if (metadata.local_surface_id) - m_localSurfaceIdAllocator.UpdateFromChild(*metadata.local_surface_id); - - m_localSurfaceId = m_localSurfaceIdAllocator.GenerateId(); - host()->SendScreenRects(); - if (m_pendingResize) { - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; - } + synchronizeVisualProperties(metadata.local_surface_id); } QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) { - return m_compositor->updatePaintNode(oldNode); -} - -void RenderWidgetHostViewQt::notifyResize() -{ - m_pendingResize = true; - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; + return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } void RenderWidgetHostViewQt::notifyShown() @@ -948,17 +923,26 @@ void RenderWidgetHostViewQt::notifyHidden() host()->WasHidden(); } -void RenderWidgetHostViewQt::windowBoundsChanged() +void RenderWidgetHostViewQt::visualPropertiesChanged() { - host()->SendScreenRects(); - if (m_delegate && m_delegate->window()) - host()->NotifyScreenInfoChanged(); -} + if (!m_delegate) + return; -void RenderWidgetHostViewQt::windowChanged() -{ - if (m_delegate && m_delegate->window()) - host()->NotifyScreenInfoChanged(); + gfx::Rect oldViewRect = m_viewRectInDips; + m_viewRectInDips = toGfx(m_delegate->viewGeometry().toAlignedRect()); + + gfx::Rect oldWindowRect = m_windowRectInDips; + QWindow *window = m_delegate->window(); + m_windowRectInDips = window ? toGfx(window->frameGeometry()) : gfx::Rect(); + + content::ScreenInfo oldScreenInfo = m_screenInfo; + m_screenInfo = screenInfoFromQScreen(window ? window->screen() : nullptr); + + if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect) + host()->SendScreenRects(); + + if (m_viewRectInDips.size() != oldViewRect.size() || m_screenInfo != oldScreenInfo) + synchronizeVisualProperties(base::nullopt); } bool RenderWidgetHostViewQt::forwardEvent(QEvent *event) @@ -1164,14 +1148,9 @@ QList<QTouchEvent::TouchPoint> RenderWidgetHostViewQt::mapTouchPointIds(const QL return outputPoints; } -float RenderWidgetHostViewQt::dpiScale() const -{ - return m_adapterClient ? m_adapterClient->dpiScale() : 1.0; -} - bool RenderWidgetHostViewQt::IsPopup() const { - return popup_type_ != blink::kWebPopupTypeNone; + return widget_type_ == content::WidgetType::kPopup; } void RenderWidgetHostViewQt::handleMouseEvent(QMouseEvent* event) @@ -1313,9 +1292,9 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev) } if (hasSelection) { - content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); - if (frameHost) - frameHost->GetFrameInputHandler()->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); + content::mojom::FrameInputHandler *frameInputHandler = getFrameInputHandler(); + if (frameInputHandler) + frameInputHandler->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); } int replacementLength = ev->replacementLength(); @@ -1392,21 +1371,11 @@ void RenderWidgetHostViewQt::handleInputMethodQueryEvent(QInputMethodQueryEvent ev->accept(); } -#ifndef QT_NO_ACCESSIBILITY -void RenderWidgetHostViewQt::accessibilityActiveChanged(bool active) -{ - if (active) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - else - content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); -} -#endif // QT_NO_ACCESSIBILITY - void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) { if (!m_wheelAckPending) { Q_ASSERT(m_pendingWheelEvents.isEmpty()); - blink::WebMouseWheelEvent webEvent = WebEventFactory::toWebWheelEvent(ev, dpiScale()); + blink::WebMouseWheelEvent webEvent = WebEventFactory::toWebWheelEvent(ev); m_wheelAckPending = (webEvent.phase != blink::WebMouseWheelEvent::kPhaseEnded); m_mouseWheelPhaseHandler.AddPhaseIfNeededAndScheduleEndEvent(webEvent, false); host()->ForwardWheelEvent(webEvent); @@ -1414,10 +1383,10 @@ void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) } if (!m_pendingWheelEvents.isEmpty()) { // Try to combine with this wheel event with the last pending one. - if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), ev, dpiScale())) + if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), ev)) return; } - m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(ev, dpiScale())); + m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(ev)); } void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState /*ack_result*/) @@ -1452,16 +1421,11 @@ void RenderWidgetHostViewQt::handleGestureEvent(QNativeGestureEvent *ev) const Qt::NativeGestureType type = ev->gestureType(); // These are the only supported gestures by Chromium so far. if (type == Qt::ZoomNativeGesture || type == Qt::SmartZoomNativeGesture) { - host()->ForwardGestureEvent(WebEventFactory::toWebGestureEvent( - ev, - static_cast<double>(dpiScale()))); + host()->ForwardGestureEvent(WebEventFactory::toWebGestureEvent(ev)); } } #endif -Q_DECLARE_LOGGING_CATEGORY(QWEBENGINE_TOUCH_HANDLING); -Q_LOGGING_CATEGORY(QWEBENGINE_TOUCH_HANDLING, "qt.webengine.touch"); - void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) { // On macOS instead of handling touch events, we use the OS provided QNativeGestureEvents. @@ -1469,7 +1433,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) if (ev->spontaneous()) { return; } else { - qCWarning(QWEBENGINE_TOUCH_HANDLING) + VLOG(1) << "Sending simulated touch events to Chromium does not work properly on macOS. " "Consider using QNativeGestureEvents or QMouseEvents."; } @@ -1486,11 +1450,35 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) eventTimestamp += m_eventsToNowDelta; QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(ev->touchPoints()); + { + ui::MotionEvent::Action action; + switch (touchPoints[0].state()) { + case Qt::TouchPointPressed: + action = ui::MotionEvent::Action::DOWN; + break; + case Qt::TouchPointMoved: + action = ui::MotionEvent::Action::MOVE; + break; + case Qt::TouchPointReleased: + action = ui::MotionEvent::Action::UP; + break; + default: + action = ui::MotionEvent::Action::NONE; + break; + } + + MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), 0); + if (m_touchSelectionController->WillHandleTouchEvent(motionEvent)) { + ev->accept(); + return; + } + } switch (ev->type()) { case QEvent::TouchBegin: m_sendMotionActionDown = true; m_touchMotionStarted = true; + m_touchSelectionControllerClient->onTouchDown(); break; case QEvent::TouchUpdate: m_touchMotionStarted = true; @@ -1510,13 +1498,13 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) if (touchPoints.isEmpty()) touchPoints = m_previousTouchPoints; clearPreviousTouchMotionState(); - MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL, - ev->modifiers(), dpiScale()); + MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL, ev->modifiers()); processMotionEvent(cancelEvent); return; } case QEvent::TouchEnd: clearPreviousTouchMotionState(); + m_touchSelectionControllerClient->onTouchUp(); break; default: break; @@ -1563,8 +1551,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) continue; } - MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), dpiScale(), - i); + MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), i); processMotionEvent(motionEvent); } } @@ -1581,7 +1568,7 @@ void RenderWidgetHostViewQt::handlePointerEvent(T *event) { // Currently WebMouseEvent is a subclass of WebPointerProperties, so basically // tablet events are mouse events with extra properties. - blink::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event, dpiScale()); + blink::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event); if ((webEvent.GetType() == blink::WebInputEvent::kMouseDown || webEvent.GetType() == blink::WebInputEvent::kMouseUp) && webEvent.button == blink::WebMouseEvent::Button::kNoButton) { // Blink can only handle the 3 main mouse-buttons and may assert when processing mouse-down for no button. @@ -1627,7 +1614,7 @@ void RenderWidgetHostViewQt::handlePointerEvent(T *event) void RenderWidgetHostViewQt::handleHoverEvent(QHoverEvent *ev) { - host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(ev, dpiScale())); + host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(ev)); } void RenderWidgetHostViewQt::handleFocusEvent(QFocusEvent *ev) @@ -1654,11 +1641,6 @@ void RenderWidgetHostViewQt::SetNeedsBeginFrames(bool needs_begin_frames) m_compositor->setNeedsBeginFrames(needs_begin_frames); } -void RenderWidgetHostViewQt::OnBeginFrame(base::TimeTicks frame_time) -{ - host()->ProgressFlingIfNeeded(frame_time); -} - content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost() { content::RenderViewHostImpl *viewHost = content::RenderViewHostImpl::From(host()); @@ -1672,6 +1654,15 @@ content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost() return focusedFrame->current_frame_host(); } +content::mojom::FrameInputHandler *RenderWidgetHostViewQt::getFrameInputHandler() +{ + content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); + if (!frameHost) + return nullptr; + + return frameHost->GetFrameInputHandler(); +} + ui::TextInputType RenderWidgetHostViewQt::getTextInputType() const { if (text_input_manager_ && text_input_manager_->GetTextInputState()) @@ -1696,7 +1687,7 @@ const viz::FrameSinkId &RenderWidgetHostViewQt::GetFrameSinkId() const const viz::LocalSurfaceId &RenderWidgetHostViewQt::GetLocalSurfaceId() const { - return m_localSurfaceId; + return m_localSurfaceIdAllocator.GetCurrentLocalSurfaceId(); } void RenderWidgetHostViewQt::TakeFallbackContentFrom(content::RenderWidgetHostView *view) @@ -1710,14 +1701,39 @@ void RenderWidgetHostViewQt::TakeFallbackContentFrom(content::RenderWidgetHostVi void RenderWidgetHostViewQt::EnsureSurfaceSynchronizedForLayoutTest() { - ++m_latestCaptureSequenceNumber; - if (host()) - host()->SynchronizeVisualProperties(); + NOTIMPLEMENTED(); } uint32_t RenderWidgetHostViewQt::GetCaptureSequenceNumber() const { - return m_latestCaptureSequenceNumber; + return 0; +} + +void RenderWidgetHostViewQt::ResetFallbackToFirstNavigationSurface() +{ + Q_UNIMPLEMENTED(); +} + +void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() +{ + content::RenderWidgetHostViewBase::OnRenderFrameMetadataChangedAfterActivation(); + + const cc::RenderFrameMetadata &metadata = host()->render_frame_metadata_provider()->LastRenderFrameMetadata(); + if (metadata.selection.start != m_selectionStart || metadata.selection.end != m_selectionEnd) { + m_selectionStart = metadata.selection.start; + m_selectionEnd = metadata.selection.end; + m_touchSelectionControllerClient->UpdateClientSelectionBounds(m_selectionStart, m_selectionEnd); + } +} + +void RenderWidgetHostViewQt::synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceId> &childSurfaceId) +{ + if (childSurfaceId) + m_localSurfaceIdAllocator.UpdateFromChild(*childSurfaceId); + else + m_localSurfaceIdAllocator.GenerateId(); + + host()->SynchronizeVisualProperties(); } } // namespace QtWebEngineCore diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index a12ffe636..70b4f509e 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -50,14 +50,12 @@ #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/renderer_host/text_input_manager.h" -#include "content/common/view_messages.h" #include "gpu/ipc/common/gpu_messages.h" #include "ui/events/gesture_detection/filtered_gesture_provider.h" #include "qtwebenginecoreglobal_p.h" #include <QMap> #include <QPoint> #include <QtGlobal> -#include <QtGui/qaccessible.h> #include <QtGui/QTouchEvent> QT_BEGIN_NAMESPACE @@ -67,11 +65,21 @@ QT_END_NAMESPACE namespace content { class RenderFrameHost; class RenderWidgetHostImpl; +namespace mojom { +class FrameInputHandler; +} +} + +namespace ui { +class TouchSelectionController; } namespace QtWebEngineCore { class Compositor; +class TouchHandleDrawableClient; +class TouchSelectionControllerClientQt; +class TouchSelectionMenuController; struct MultipleMouseClickHelper { @@ -94,9 +102,6 @@ class RenderWidgetHostViewQt , public ui::GestureProviderClient , public RenderWidgetHostViewQtDelegateClient , public base::SupportsWeakPtr<RenderWidgetHostViewQt> -#ifndef QT_NO_ACCESSIBILITY - , public QAccessible::ActivationObserver -#endif // QT_NO_ACCESSIBILITY , public content::TextInputManager::Observer { public: @@ -111,15 +116,14 @@ public: RenderWidgetHostViewQtDelegate *delegate() { return m_delegate.get(); } void setDelegate(RenderWidgetHostViewQtDelegate *delegate); + WebContentsAdapterClient *adapterClient() { return m_adapterClient; } void setAdapterClient(WebContentsAdapterClient *adapterClient); - void OnBeginFrame(base::TimeTicks frame_time); void InitAsChild(gfx::NativeView) override; void InitAsPopup(content::RenderWidgetHostView*, const gfx::Rect&) override; void InitAsFullscreen(content::RenderWidgetHostView*) override; void SetSize(const gfx::Size& size) override; void SetBounds(const gfx::Rect&) override; - gfx::Size GetCompositorViewportPixelSize() const override; gfx::NativeView GetNativeView() const override; gfx::NativeViewAccessible GetNativeViewAccessible() override; void Focus() override; @@ -163,17 +167,17 @@ public: void TakeFallbackContentFrom(content::RenderWidgetHostView *view) override; void EnsureSurfaceSynchronizedForLayoutTest() override; uint32_t GetCaptureSequenceNumber() const override; + void ResetFallbackToFirstNavigationSurface() override; + void DidStopFlinging() override; // Overridden from ui::GestureProviderClient. void OnGestureEvent(const ui::GestureEventData& gesture) override; // Overridden from RenderWidgetHostViewQtDelegateClient. QSGNode *updatePaintNode(QSGNode *) override; - void notifyResize() override; void notifyShown() override; void notifyHidden() override; - void windowBoundsChanged() override; - void windowChanged() override; + void visualPropertiesChanged() override; bool forwardEvent(QEvent *) override; QVariant inputMethodQuery(Qt::InputMethodQuery query) override; void closePopup() override; @@ -209,27 +213,37 @@ public: // Overridden from content::BrowserAccessibilityDelegate content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame) override; -#ifndef QT_NO_ACCESSIBILITY - void accessibilityActiveChanged(bool active) override; -#endif // QT_NO_ACCESSIBILITY LoadVisuallyCommittedState getLoadVisuallyCommittedState() const { return m_loadVisuallyCommittedState; } void setLoadVisuallyCommittedState(LoadVisuallyCommittedState state) { m_loadVisuallyCommittedState = state; } + // Overridden from content::RenderFrameMetadataProvider::Observer + void OnRenderFrameMetadataChangedAfterActivation() override; + gfx::SizeF lastContentsSize() const { return m_lastContentsSize; } gfx::Vector2dF lastScrollOffset() const { return m_lastScrollOffset; } + ui::TouchSelectionController *getTouchSelectionController() const { return m_touchSelectionController.get(); } + TouchSelectionControllerClientQt *getTouchSelectionControllerClient() const { return m_touchSelectionControllerClient.get(); } + content::mojom::FrameInputHandler *getFrameInputHandler(); + ui::TextInputType getTextInputType() const; + private: void processMotionEvent(const ui::MotionEvent &motionEvent); void clearPreviousTouchMotionState(); QList<QTouchEvent::TouchPoint> mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints); - float dpiScale() const; - void updateNeedsBeginFramesInternal(); bool IsPopup() const; void selectionChanged(); content::RenderFrameHost *getFocusedFrameHost(); - ui::TextInputType getTextInputType() const; + + void synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceId> &childSurfaceId); + + // Geometry of the view in screen DIPs. + gfx::Rect m_viewRectInDips; + // Geometry of the window, including frame, in screen DIPs. + gfx::Rect m_windowRectInDips; + content::ScreenInfo m_screenInfo; ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; @@ -252,7 +266,6 @@ private: gfx::Vector2dF m_lastScrollOffset; gfx::SizeF m_lastContentsSize; - viz::LocalSurfaceId m_localSurfaceId; viz::ParentLocalSurfaceIdAllocator m_localSurfaceIdAllocator; uint m_imState; @@ -265,13 +278,16 @@ private: bool m_imeHasHiddenTextCapability; bool m_wheelAckPending; - bool m_pendingResize; QList<blink::WebMouseWheelEvent> m_pendingWheelEvents; content::MouseWheelPhaseHandler m_mouseWheelPhaseHandler; viz::FrameSinkId m_frameSinkId; - uint32_t m_latestCaptureSequenceNumber = 0u; std::string m_editCommand; + + std::unique_ptr<TouchSelectionControllerClientQt> m_touchSelectionControllerClient; + std::unique_ptr<ui::TouchSelectionController> m_touchSelectionController; + gfx::SelectionBound m_selectionStart; + gfx::SelectionBound m_selectionEnd; }; } // namespace QtWebEngineCore diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h index 5ce595502..e2c98360d 100644 --- a/src/core/render_widget_host_view_qt_delegate.h +++ b/src/core/render_widget_host_view_qt_delegate.h @@ -78,11 +78,9 @@ class QWEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegateClient { public: virtual ~RenderWidgetHostViewQtDelegateClient() { } virtual QSGNode *updatePaintNode(QSGNode *) = 0; - virtual void notifyResize() = 0; virtual void notifyShown() = 0; virtual void notifyHidden() = 0; - virtual void windowBoundsChanged() = 0; - virtual void windowChanged() = 0; + virtual void visualPropertiesChanged() = 0; virtual bool forwardEvent(QEvent *) = 0; virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) = 0; virtual void closePopup() = 0; @@ -92,8 +90,7 @@ class QWEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegate { public: virtual ~RenderWidgetHostViewQtDelegate() { } virtual void initAsPopup(const QRect&) = 0; - virtual QRectF screenRect() const = 0; - virtual QRectF contentsRect() const = 0; + virtual QRectF viewGeometry() const = 0; virtual void setKeyboardFocus() = 0; virtual bool hasKeyboardFocus() = 0; virtual void lockMouse() = 0; diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp index 403448b91..296e78b07 100644 --- a/src/core/renderer/content_renderer_client_qt.cpp +++ b/src/core/renderer/content_renderer_client_qt.cpp @@ -40,6 +40,8 @@ #include "renderer/content_renderer_client_qt.h" #include "common/qt_messages.h" +#include "extensions/buildflags/buildflags.h" +#include "printing/buildflags/buildflags.h" #include "renderer/content_settings_observer_qt.h" #include "base/strings/string_split.h" #if QT_CONFIG(webengine_spellchecker) @@ -85,6 +87,12 @@ #if QT_CONFIG(webengine_webchannel) #include "renderer/web_channel_ipc_transport.h" #endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "common/extensions/extensions_client_qt.h" +#include "extensions/extensions_renderer_client_qt.h" +#endif //ENABLE_EXTENSIONS + #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/connector.h" @@ -106,6 +114,10 @@ static const char kHttpErrorDomain[] = "http"; ContentRendererClientQt::ContentRendererClientQt() { +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionsClient::Set(extensions::ExtensionsClientQt::GetInstance()); + extensions::ExtensionsRendererClient::Set(ExtensionsRendererClientQt::GetInstance()); +#endif } ContentRendererClientQt::~ContentRendererClientQt() @@ -137,7 +149,11 @@ void ContentRendererClientQt::RenderThreadStarted() // Allow XMLHttpRequests from qrc to file. blink::WebURL qrc(blink::KURL("qrc:")); blink::WebString file(blink::WebString::FromASCII("file")); - blink::WebSecurityPolicy::AddOriginAccessWhitelistEntry(qrc, file, blink::WebString(), true); + blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(qrc, file, blink::WebString(), true, + network::mojom::CORSOriginAccessMatchPriority::kDefaultPriority); +#if BUILDFLAG(ENABLE_EXTENSIONS) + ExtensionsRendererClientQt::GetInstance()->RenderThreadStarted(); +#endif } void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view) @@ -149,11 +165,12 @@ void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame* render_frame) { - new QtWebEngineCore::RenderFrameObserverQt(render_frame); + QtWebEngineCore::RenderFrameObserverQt *render_frame_observer = new QtWebEngineCore::RenderFrameObserverQt(render_frame); #if QT_CONFIG(webengine_webchannel) if (render_frame->IsMainFrame()) new WebChannelIPCTransport(render_frame); #endif + UserResourceController::instance()->renderFrameCreated(render_frame); new QtWebEngineCore::ContentSettingsObserverQt(render_frame); @@ -166,17 +183,41 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame* render_fr render_frame, base::WrapUnique(new PrintWebViewHelperDelegateQt())); #endif // QT_CONFIG(webengine_printing_and_pdf) +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto registry = std::make_unique<service_manager::BinderRegistry>(); + ExtensionsRendererClientQt::GetInstance()->RenderFrameCreated(render_frame, render_frame_observer->registry()); +#endif } -void ContentRendererClientQt::RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) +void ContentRendererClientQt::RunScriptsAtDocumentStart(content::RenderFrame *render_frame) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + ExtensionsRendererClientQt::GetInstance()->RunScriptsAtDocumentStart(render_frame); + // |render_frame| might be dead by now. +#endif +} + +void ContentRendererClientQt::RunScriptsAtDocumentEnd(content::RenderFrame *render_frame) { // Check whether the render_frame has been created and has not been detached yet. // Otherwise the WebFrame is not available. RenderFrameObserverQt *render_frame_observer = RenderFrameObserverQt::Get(render_frame); - if (!render_frame_observer || render_frame_observer->isFrameDetached()) - return; // The frame is invisible to scripts. - UserResourceController::instance()->RunScriptsAtDocumentEnd(render_frame); + if (render_frame_observer && !render_frame_observer->isFrameDetached()) + UserResourceController::instance()->RunScriptsAtDocumentEnd(render_frame); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + ExtensionsRendererClientQt::GetInstance()->RunScriptsAtDocumentEnd(render_frame); + // |render_frame| might be dead by now. +#endif +} + +void ContentRendererClientQt::RunScriptsAtDocumentIdle(content::RenderFrame *render_frame) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + ExtensionsRendererClientQt::GetInstance()->RunScriptsAtDocumentIdle(render_frame); + // |render_frame| might be dead by now. +#endif } bool ContentRendererClientQt::HasErrorPage(int httpStatusCode) @@ -230,6 +271,7 @@ void ContentRendererClientQt::GetNavigationErrorStringsInternal(content::RenderF error_page::LocalizedError::GetStrings( error.reason(), error.domain(), error.url(), isPost, error.stale_copy_in_cache(), false, false, + error_page::LocalizedError::OfflineContentOnNetErrorFeatureState::kDisabled, locale, std::unique_ptr<error_page::ErrorPageParams>(), &errorStrings); resourceId = IDR_NET_ERROR_HTML; @@ -259,6 +301,29 @@ blink::WebPrescientNetworking *ContentRendererClientQt::GetPrescientNetworking() return m_prescientNetworkingDispatcher.get(); } +bool ContentRendererClientQt::OverrideCreatePlugin( + content::RenderFrame* render_frame, + const blink::WebPluginParams& params, blink::WebPlugin** plugin) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!ExtensionsRendererClientQt::GetInstance()->OverrideCreatePlugin(render_frame, params)) + return false; +#endif //ENABLE_EXTENSIONS + return content::ContentRendererClient::OverrideCreatePlugin(render_frame, params, plugin); +} + +content::BrowserPluginDelegate* ContentRendererClientQt::CreateBrowserPluginDelegate(content::RenderFrame *render_frame, + const content::WebPluginInfo &info, + const std::string &mime_type, + const GURL &original_url) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ExtensionsRendererClientQt::GetInstance()->CreateBrowserPluginDelegate(render_frame, info, mime_type, original_url); +#else + return nullptr; +#endif +} + void ContentRendererClientQt::OnStart() { context()->connector()->BindConnectorRequest(std::move(m_connectorRequest)); @@ -487,6 +552,21 @@ void ContentRendererClientQt::InitSpellCheck() } #endif +void ContentRendererClientQt::WillSendRequest(blink::WebLocalFrame *frame, + ui::PageTransition transition_type, + const blink::WebURL &url, + const url::Origin *initiator_origin, + GURL *new_url, + bool *attach_same_site_cookies) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + ExtensionsRendererClientQt::GetInstance()->WillSendRequest(frame, transition_type, url, initiator_origin, new_url, attach_same_site_cookies); + if (!new_url->is_empty()) + return; +#endif + content::ContentRendererClient::WillSendRequest(frame, transition_type, url, initiator_origin, new_url, attach_same_site_cookies); +} + void ContentRendererClientQt::CreateRendererService(service_manager::mojom::ServiceRequest service_request) { m_serviceContext = std::make_unique<service_manager::ServiceContext>( diff --git a/src/core/renderer/content_renderer_client_qt.h b/src/core/renderer/content_renderer_client_qt.h index 2a353caa6..8955a3797 100644 --- a/src/core/renderer/content_renderer_client_qt.h +++ b/src/core/renderer/content_renderer_client_qt.h @@ -96,7 +96,20 @@ public: blink::WebPrescientNetworking* GetPrescientNetworking() override; void AddSupportedKeySystems(std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) override; - void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; + void RunScriptsAtDocumentStart(content::RenderFrame *render_frame) override; + void RunScriptsAtDocumentEnd(content::RenderFrame *render_frame) override; + void RunScriptsAtDocumentIdle(content::RenderFrame *render_frame) override; + bool OverrideCreatePlugin(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, blink::WebPlugin** plugin) override; + content::BrowserPluginDelegate* CreateBrowserPluginDelegate(content::RenderFrame* render_frame, + const content::WebPluginInfo& info, const std::string& mime_type, const GURL& original_url) override; + + void WillSendRequest(blink::WebLocalFrame *frame, + ui::PageTransition transition_type, + const blink::WebURL &url, + const url::Origin *initiator_origin, + GURL *new_url, + bool *attach_same_site_cookies) override; void CreateRendererService(service_manager::mojom::ServiceRequest service_request) override; diff --git a/src/core/renderer/content_settings_observer_qt.cpp b/src/core/renderer/content_settings_observer_qt.cpp index 045098457..c1495e1fe 100644 --- a/src/core/renderer/content_settings_observer_qt.cpp +++ b/src/core/renderer/content_settings_observer_qt.cpp @@ -93,8 +93,8 @@ bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message& message) return handled; } -void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool /*is_new_navigation*/, - bool is_same_document_navigation) +void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition /*transition*/) { blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); if (frame->Parent()) diff --git a/src/core/renderer/content_settings_observer_qt.h b/src/core/renderer/content_settings_observer_qt.h index 981655f20..a9bee3d2d 100644 --- a/src/core/renderer/content_settings_observer_qt.h +++ b/src/core/renderer/content_settings_observer_qt.h @@ -80,8 +80,8 @@ private: // RenderFrameObserver implementation: bool OnMessageReceived(const IPC::Message &message) override; - void DidCommitProvisionalLoad(bool is_new_navigation, - bool is_same_document_navigation) override; + void DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition transition) override; void OnDestruct() override; // Message handlers. diff --git a/src/core/chromium_overrides.h b/src/core/renderer/extensions/extensions_dispatcher_delegate_qt.cpp index b27bf309c..418429330 100644 --- a/src/core/chromium_overrides.h +++ b/src/core/renderer/extensions/extensions_dispatcher_delegate_qt.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,18 +37,16 @@ ** ****************************************************************************/ -#ifndef CHROMIUM_OVERRIDES_H -#define CHROMIUM_OVERRIDES_H +#include "extensions_dispatcher_delegate_qt.h" -#include "content/public/common/screen_info.h" -#include <QtGlobal> +namespace QtWebEngineCore { -QT_BEGIN_NAMESPACE -class QWindow; -QT_END_NAMESPACE +ExtensionsDispatcherDelegateQt::ExtensionsDispatcherDelegateQt() +{ +} -namespace QtWebEngineCore { -void GetScreenInfoFromNativeWindow(QWindow* window, content::ScreenInfo* results); +ExtensionsDispatcherDelegateQt::~ExtensionsDispatcherDelegateQt() +{ } -#endif +} //namespace QtWebEngineCore diff --git a/src/core/renderer/extensions/extensions_dispatcher_delegate_qt.h b/src/core/renderer/extensions/extensions_dispatcher_delegate_qt.h new file mode 100644 index 000000000..25aa18e71 --- /dev/null +++ b/src/core/renderer/extensions/extensions_dispatcher_delegate_qt.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXTENSIONSDISPATCHERDELEGATEQT_H +#define EXTENSIONSDISPATCHERDELEGATEQT_H + +#include "base/macros.h" +#include "extensions/renderer/dispatcher_delegate.h" + +namespace QtWebEngineCore { + +class ExtensionsDispatcherDelegateQt : public extensions::DispatcherDelegate +{ +public: + ExtensionsDispatcherDelegateQt(); + ~ExtensionsDispatcherDelegateQt() override; + +private: + DISALLOW_COPY_AND_ASSIGN(ExtensionsDispatcherDelegateQt); +}; + +} // namespace QtWebEngineCore + +#endif // EXTENSIONSDISPATCHERDELEGATEQT_H diff --git a/src/core/renderer/extensions/extensions_renderer_client_qt.cpp b/src/core/renderer/extensions/extensions_renderer_client_qt.cpp new file mode 100644 index 000000000..c41d9e6a4 --- /dev/null +++ b/src/core/renderer/extensions/extensions_renderer_client_qt.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// based on chrome/renderer/extensions/chrome_extensions_renderer_client.cc: +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions_renderer_client_qt.h" + +#include "extensions_dispatcher_delegate_qt.h" +#include "renderer_permissions_policy_delegate_qt.h" +#include "resource_request_policy_qt.h" + +#include "base/command_line.h" +#include "base/lazy_instance.h" +#include "content/public/common/content_constants.h" +#include "content/public/common/content_switches.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_thread.h" +#include "extensions/common/constants.h" +#include "extensions/common/switches.h" +#include "extensions/renderer/dispatcher.h" +#include "extensions/renderer/extension_frame_helper.h" +#include "extensions/renderer/extensions_render_frame_observer.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h" +#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" +#include "extensions/renderer/renderer_extension_registry.h" +#include "extensions/renderer/script_context.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/web/web_plugin_params.h" + +namespace chrome { +const char kExtensionInvalidRequestURL[] = "chrome-extension://invalid/"; +const char kExtensionResourceInvalidRequestURL[] = "chrome-extension-resource://invalid/"; +} + +namespace QtWebEngineCore { + +ExtensionsRendererClientQt::ExtensionsRendererClientQt() +{ +} + +ExtensionsRendererClientQt::~ExtensionsRendererClientQt() +{ +} + +// Returns true if the current render process was launched incognito. +bool ExtensionsRendererClientQt::IsIncognitoProcess() const +{ + return false; +} + +// Returns the lowest isolated world ID available to extensions. +// Must be greater than 0. See blink::WebFrame::executeScriptInIsolatedWorld +// (third_party/WebKit/public/web/WebFrame.h) for additional context. +int ExtensionsRendererClientQt::GetLowestIsolatedWorldId() const +{ + return 257; +} + +// static +ExtensionsRendererClientQt *ExtensionsRendererClientQt::GetInstance() +{ + static base::LazyInstance<ExtensionsRendererClientQt>::Leaky client = + LAZY_INSTANCE_INITIALIZER; + return client.Pointer(); +} + +extensions::Dispatcher *ExtensionsRendererClientQt::GetDispatcher() +{ + return extension_dispatcher_.get(); +} + +void ExtensionsRendererClientQt::OnExtensionLoaded(const extensions::Extension &extension) +{ + resource_request_policy_->OnExtensionLoaded(extension); +} + +void ExtensionsRendererClientQt::OnExtensionUnloaded(const extensions::ExtensionId &extension_id) +{ + resource_request_policy_->OnExtensionUnloaded(extension_id); +} + +void ExtensionsRendererClientQt::RenderThreadStarted() +{ + content::RenderThread *thread = content::RenderThread::Get(); + // ChromeRenderViewTest::SetUp() creates its own ExtensionDispatcher and + // injects it using SetExtensionDispatcher(). Don't overwrite it. + if (!extension_dispatcher_) + extension_dispatcher_.reset(new extensions::Dispatcher(std::make_unique<ExtensionsDispatcherDelegateQt>())); + permissions_policy_delegate_.reset(new RendererPermissionsPolicyDelegateQt(extension_dispatcher_.get())); + resource_request_policy_.reset(new extensions::ResourceRequestPolicyQt(extension_dispatcher_.get())); + guest_view_container_dispatcher_.reset(new extensions::ExtensionsGuestViewContainerDispatcher()); + + thread->AddObserver(extension_dispatcher_.get()); + thread->AddObserver(guest_view_container_dispatcher_.get()); +} + +void ExtensionsRendererClientQt::RenderFrameCreated(content::RenderFrame *render_frame, + service_manager::BinderRegistry *registry) +{ + new extensions::ExtensionsRenderFrameObserver(render_frame, registry); + new extensions::ExtensionFrameHelper(render_frame, + extension_dispatcher_.get()); + extension_dispatcher_->OnRenderFrameCreated(render_frame); +} + +bool ExtensionsRendererClientQt::OverrideCreatePlugin(content::RenderFrame *render_frame, + const blink::WebPluginParams ¶ms) +{ + if (params.mime_type.Utf8() != content::kBrowserPluginMimeType) + return true; + bool guest_view_api_available = false; + return !guest_view_api_available; +} + +void ExtensionsRendererClientQt::WillSendRequest(blink::WebLocalFrame *frame, + ui::PageTransition transition_type, + const blink::WebURL &url, + const url::Origin *initiator_origin, + GURL *new_url, + bool *attach_same_site_cookies) +{ + if (url.ProtocolIs(extensions::kExtensionScheme) && + !resource_request_policy_->CanRequestResource(url, frame, transition_type)) { + *new_url = GURL(chrome::kExtensionInvalidRequestURL); + } +} + +bool ExtensionsRendererClientQt::ShouldFork(blink::WebLocalFrame *frame, + const GURL &url, + bool is_initial_navigation, + bool is_server_redirect, + bool *send_referrer) +{ + return false; // TODO: Fix this to a sensible value +} + +content::BrowserPluginDelegate *ExtensionsRendererClientQt::CreateBrowserPluginDelegate(content::RenderFrame *render_frame, + const content::WebPluginInfo &info, + const std::string &mime_type, + const GURL &original_url) +{ + if (mime_type == content::kBrowserPluginMimeType) + return new extensions::ExtensionsGuestViewContainer(render_frame); + return new extensions::MimeHandlerViewContainer(render_frame, info, mime_type, original_url); +} + +void ExtensionsRendererClientQt::RunScriptsAtDocumentStart(content::RenderFrame *render_frame) +{ + extension_dispatcher_->RunScriptsAtDocumentStart(render_frame); +} + +void ExtensionsRendererClientQt::RunScriptsAtDocumentEnd(content::RenderFrame *render_frame) +{ + extension_dispatcher_->RunScriptsAtDocumentEnd(render_frame); +} + +void ExtensionsRendererClientQt::RunScriptsAtDocumentIdle(content::RenderFrame *render_frame) +{ + extension_dispatcher_->RunScriptsAtDocumentIdle(render_frame); +} + + +} // namespace QtWebEngineCore diff --git a/src/core/renderer/extensions/extensions_renderer_client_qt.h b/src/core/renderer/extensions/extensions_renderer_client_qt.h new file mode 100644 index 000000000..2d45d255a --- /dev/null +++ b/src/core/renderer/extensions/extensions_renderer_client_qt.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXTENSIONSRENDERERCLIENTQT_H +#define EXTENSIONSRENDERERCLIENTQT_H + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "extensions/renderer/extensions_renderer_client.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "ui/base/page_transition_types.h" + +class GURL; + +namespace blink { +class WebLocalFrame; +struct WebPluginParams; +class WebURL; +} + +namespace content { +class BrowserPluginDelegate; +class RenderFrame; +class RenderView; +struct WebPluginInfo; +} + +namespace url { +class Origin; +} + +namespace extensions { +class Dispatcher; +class ExtensionsGuestViewContainerDispatcher; +class ResourceRequestPolicyQt; +} + +namespace QtWebEngineCore { + +class ExtensionsDispatcherDelegateQt; +class RendererPermissionsPolicyDelegateQt; + +class ExtensionsRendererClientQt : public extensions::ExtensionsRendererClient +{ +public: + ExtensionsRendererClientQt(); + ~ExtensionsRendererClientQt() override; + + // extensions::ExtensionsRendererClient implementation. + bool IsIncognitoProcess() const override; + int GetLowestIsolatedWorldId() const override; + extensions::Dispatcher *GetDispatcher() override; + void OnExtensionLoaded(const extensions::Extension &extension) override; + void OnExtensionUnloaded(const extensions::ExtensionId &extension_id) override; + + // Match ContentRendererClientQt's method names... + void RenderThreadStarted(); + void RenderFrameCreated(content::RenderFrame *, service_manager::BinderRegistry *); + bool OverrideCreatePlugin(content::RenderFrame *render_frame, + const blink::WebPluginParams ¶ms); + void WillSendRequest(blink::WebLocalFrame *frame, + ui::PageTransition transition_type, + const blink::WebURL &url, + const url::Origin *initiator_origin, + GURL *new_url, + bool *attach_same_site_cookies); + + static bool ShouldFork(blink::WebLocalFrame *frame, + const GURL &url, + bool is_initial_navigation, + bool is_server_redirect, + bool *send_referrer); + static content::BrowserPluginDelegate *CreateBrowserPluginDelegate(content::RenderFrame *render_frame, + const content::WebPluginInfo &info, + const std::string &mime_type, + const GURL &original_url); + + void RunScriptsAtDocumentStart(content::RenderFrame *render_frame); + void RunScriptsAtDocumentEnd(content::RenderFrame *render_frame); + void RunScriptsAtDocumentIdle(content::RenderFrame *render_frame); + + extensions::Dispatcher *extension_dispatcher() + { return extension_dispatcher_.get(); } + + static ExtensionsRendererClientQt *GetInstance(); + +private: + std::unique_ptr<ExtensionsDispatcherDelegateQt> extension_dispatcher_delegate_; + std::unique_ptr<RendererPermissionsPolicyDelegateQt> permissions_policy_delegate_; + std::unique_ptr<extensions::Dispatcher> extension_dispatcher_; + std::unique_ptr<extensions::ExtensionsGuestViewContainerDispatcher> guest_view_container_dispatcher_; + std::unique_ptr<extensions::ResourceRequestPolicyQt> resource_request_policy_; +}; + +} // namespace QtWebEngineCore + +#endif // EXTENSIONSRENDERERCLIENTQT_H diff --git a/src/core/renderer/extensions/renderer_permissions_policy_delegate_qt.cpp b/src/core/renderer/extensions/renderer_permissions_policy_delegate_qt.cpp new file mode 100644 index 000000000..39412b76c --- /dev/null +++ b/src/core/renderer/extensions/renderer_permissions_policy_delegate_qt.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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 "renderer_permissions_policy_delegate_qt.h" + +#include "extensions/common/constants.h" +#include "extensions/common/extensions_client.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/switches.h" +#include "extensions/renderer/dispatcher.h" + +namespace QtWebEngineCore { + +RendererPermissionsPolicyDelegateQt::RendererPermissionsPolicyDelegateQt(extensions::Dispatcher *dispatcher) + : m_dispatcher(dispatcher) +{ + extensions::PermissionsData::SetPolicyDelegate(this); +} + +RendererPermissionsPolicyDelegateQt::~RendererPermissionsPolicyDelegateQt() +{ + extensions::PermissionsData::SetPolicyDelegate(nullptr); +} + +bool RendererPermissionsPolicyDelegateQt::IsRestrictedUrl(const GURL &, std::string *) +{ + return false; +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/qrc_protocol_handler_qt.h b/src/core/renderer/extensions/renderer_permissions_policy_delegate_qt.h index f2849c1ef..e2af47657 100644 --- a/src/core/net/qrc_protocol_handler_qt.h +++ b/src/core/renderer/extensions/renderer_permissions_policy_delegate_qt.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,34 +37,32 @@ ** ****************************************************************************/ -#ifndef QRC_PROTOCOL_HANDLER_QT_H_ -#define QRC_PROTOCOL_HANDLER_QT_H_ +#ifndef RENDERERPERMISSIONSPOLICYDELEGATEQT_H +#define RENDERERPERMISSIONSPOLICYDELEGATEQT_H -#include "net/url_request/url_request_job_factory.h" +#include "base/macros.h" +#include "extensions/common/permissions/permissions_data.h" -namespace net { - -class NetworkDelegate; -class URLRequestJob; - -} // namespace +namespace extensions { +class Dispatcher; +} namespace QtWebEngineCore { -extern const char kQrcSchemeQt[]; - -// Implements a ProtocolHandler for qrc file jobs. If |network_delegate_| is NULL, -// then all file requests will fail with ERR_ACCESS_DENIED. -class QrcProtocolHandlerQt : public net::URLRequestJobFactory::ProtocolHandler { - +class RendererPermissionsPolicyDelegateQt : public extensions::PermissionsData::PolicyDelegate +{ public: - QrcProtocolHandlerQt(); - net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override; + explicit RendererPermissionsPolicyDelegateQt(extensions::Dispatcher *dispatcher); + ~RendererPermissionsPolicyDelegateQt() override; + + bool IsRestrictedUrl(const GURL &, std::string *) override; private: - DISALLOW_COPY_AND_ASSIGN(QrcProtocolHandlerQt); + extensions::Dispatcher *m_dispatcher; + + DISALLOW_COPY_AND_ASSIGN(RendererPermissionsPolicyDelegateQt); }; } // namespace QtWebEngineCore -#endif // QRC_PROTOCOL_HANDLER_QT_H_ +#endif // RENDERERPERMISSIONSPOLICYDELEGATEQT_H diff --git a/src/core/renderer/extensions/resource_request_policy_qt.cpp b/src/core/renderer/extensions/resource_request_policy_qt.cpp new file mode 100644 index 000000000..dc5a90120 --- /dev/null +++ b/src/core/renderer/extensions/resource_request_policy_qt.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// based on chrome/renderer/extensions/resource_request_policy.cc: +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "resource_request_policy_qt.h" + +#include "base/strings/stringprintf.h" +#include "chrome/common/url_constants.h" +#include "extensions/common/constants.h" +#include "extensions/common/manifest_handlers/web_accessible_resources_info.h" +#include "extensions/common/manifest_handlers/webview_info.h" +#include "extensions/renderer/dispatcher.h" +#include "third_party/blink/public/web/web_console_message.h" +#include "third_party/blink/public/web/web_document.h" +#include "third_party/blink/public/web/web_local_frame.h" + +namespace extensions { +ResourceRequestPolicyQt::ResourceRequestPolicyQt(Dispatcher *dispatcher) + : m_dispatcher(dispatcher) +{ +} + +void ResourceRequestPolicyQt::OnExtensionLoaded(const Extension &extension) +{ + if (WebAccessibleResourcesInfo::HasWebAccessibleResources(&extension) + || WebviewInfo::HasWebviewAccessibleResources(extension, m_dispatcher->webview_partition_id()) +// // Hosted app icons are accessible. +// // TODO(devlin): Should we incorporate this into +// // WebAccessibleResourcesInfo? +// || (extension.is_hosted_app() && !IconsInfo::GetIcons(&extension).empty()) + ) { + m_web_accessible_ids.insert(extension.id()); + } +} + +void ResourceRequestPolicyQt::OnExtensionUnloaded(const ExtensionId &extension_id) +{ + m_web_accessible_ids.erase(extension_id); +} + +// Returns true if the chrome-extension:// |resource_url| can be requested +// from |frame_url|. In some cases this decision is made based upon how +// this request was generated. Web triggered transitions are more restrictive +// than those triggered through UI. +bool ResourceRequestPolicyQt::CanRequestResource(const GURL &resource_url, + blink::WebLocalFrame *frame, + ui::PageTransition transition_type) +{ + CHECK(resource_url.SchemeIs(kExtensionScheme)); + + GURL frame_url = frame->GetDocument().Url(); + + // The page_origin may be GURL("null") for unique origins like data URLs, + // but this is ok for the checks below. We only care if it matches the + // current extension or has a devtools scheme. + GURL page_origin = url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(); + + GURL extension_origin = resource_url.GetOrigin(); + + // We always allow loads in the following cases, regardless of web accessible + // resources: + + // Empty urls (needed for some edge cases when we have empty urls). + if (frame_url.is_empty()) + return true; + + // Extensions requesting their own resources (frame_url check is for images, + // page_url check is for iframes). + // TODO(devlin): We should be checking the ancestor chain, not just the + // top-level frame. Additionally, we should be checking the security origin + // of the frame, to account for about:blank subframes being scripted by an + // extension parent (though we'll still need the frame origin check for + // sandboxed frames). + if (frame_url.GetOrigin() == extension_origin || page_origin == extension_origin) + return true; + + if (!ui::PageTransitionIsWebTriggerable(transition_type)) + return true; + + // Unreachable web page error page (to allow showing the icon of the + // unreachable app on this page). + if (frame_url == content::kUnreachableWebDataURL) + return true; + + bool is_dev_tools = page_origin.SchemeIs(content::kChromeDevToolsScheme); + // Note: we check |web_accessible_ids_| (rather than first looking up the + // extension in the registry and checking that) to be more resistant against + // timing attacks. This way, determining access for an extension that isn't + // installed takes the same amount of time as determining access for an + // extension with no web accessible resources. We aren't worried about any + // extensions with web accessible resources, since those are inherently + // identifiable. + if (!is_dev_tools && !m_web_accessible_ids.count(extension_origin.host())) + return false; + + const Extension* extension = RendererExtensionRegistry::Get()->GetExtensionOrAppByURL(resource_url); + if (is_dev_tools) { + // Allow the load in the case of a non-existent extension. We'll just get a + // 404 from the browser process. + // TODO(devlin): Can this happen? Does devtools potentially make requests + // to non-existent extensions? + if (!extension) + return true; +// // Devtools (chrome-extension:// URLs are loaded into frames of devtools to +// // support the devtools extension APIs). +// if (!chrome_manifest_urls::GetDevToolsPage(extension).is_empty()) +// return true; + } + + DCHECK(extension); + + // Disallow loading of packaged resources for hosted apps. We don't allow + // hybrid hosted/packaged apps. The one exception is access to icons, since + // some extensions want to be able to do things like create their own + // launchers. + base::StringPiece resource_root_relative_path = + resource_url.path_piece().empty() ? base::StringPiece() + : resource_url.path_piece().substr(1); + if (extension->is_hosted_app() /*&& !IconsInfo::GetIcons(extension).ContainsPath(resource_root_relative_path)*/) { + LOG(ERROR) << "Denying load of " << resource_url.spec() << " from " + << "hosted app."; + return false; + } + + // Disallow loading of extension resources which are not explicitly listed + // as web or WebView accessible if the manifest version is 2 or greater. + if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(extension, resource_url.path()) && + !WebviewInfo::IsResourceWebviewAccessible(extension, m_dispatcher->webview_partition_id(), resource_url.path())) + { + std::string message = base::StringPrintf( + "Denying load of %s. Resources must be listed in the " + "web_accessible_resources manifest key in order to be loaded by " + "pages outside the extension.", + resource_url.spec().c_str()); + frame->AddMessageToConsole(blink::WebConsoleMessage(blink::WebConsoleMessage::kLevelError, blink::WebString::FromUTF8(message))); + return false; + } + + return true; +} + +} // namespace extensions diff --git a/src/core/renderer/extensions/resource_request_policy_qt.h b/src/core/renderer/extensions/resource_request_policy_qt.h new file mode 100644 index 000000000..e6d4e79bb --- /dev/null +++ b/src/core/renderer/extensions/resource_request_policy_qt.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RESOURCEREQUESTPOLICYQT_H +#define RESOURCEREQUESTPOLICYQT_H + +#include <set> + +#include "base/macros.h" +#include "extensions/common/extension_id.h" +#include "ui/base/page_transition_types.h" + +class GURL; + +namespace blink { +class WebLocalFrame; +} + +namespace extensions { + +class Dispatcher; +class Extension; + +// Encapsulates the policy for when chrome-extension:// and +// chrome-extension-resource:// URLs can be requested. +class ResourceRequestPolicyQt +{ +public: + explicit ResourceRequestPolicyQt(Dispatcher *dispatcher); + + void OnExtensionLoaded(const Extension &extension); + void OnExtensionUnloaded(const ExtensionId &extension); + + // Returns true if the chrome-extension:// |resource_url| can be requested + // from |frame_url|. In some cases this decision is made based upon how + // this request was generated. Web triggered transitions are more restrictive + // than those triggered through UI. + bool CanRequestResource(const GURL &resource_url, + blink::WebLocalFrame *frame, + ui::PageTransition transition_type); + +private: + Dispatcher *m_dispatcher; + + // The set of extension IDs with any potentially web- or webview-accessible + // resources. + std::set<ExtensionId> m_web_accessible_ids; + + DISALLOW_COPY_AND_ASSIGN(ResourceRequestPolicyQt); +}; +} // namespace extensions + +#endif // RESOURCEREQUESTPOLICYQT_H diff --git a/src/core/renderer/pepper/pepper_renderer_host_factory_qt.cpp b/src/core/renderer/pepper/pepper_renderer_host_factory_qt.cpp index 4acf69043..33c744f13 100644 --- a/src/core/renderer/pepper/pepper_renderer_host_factory_qt.cpp +++ b/src/core/renderer/pepper/pepper_renderer_host_factory_qt.cpp @@ -44,9 +44,13 @@ #include "pepper_renderer_host_factory_qt.h" #include "pepper_flash_renderer_host_qt.h" +#include "qtwebenginecoreglobal_p.h" #include "base/memory/ptr_util.h" #include "chrome/renderer/pepper/pepper_flash_font_file_host.h" +#if QT_CONFIG(webengine_printing_and_pdf) +#include "components/pdf/renderer/pepper_pdf_host.h" +#endif // QT_CONFIG(webengine_printing_and_pdf) #include "content/public/renderer/renderer_ppapi_host.h" #include "ppapi/host/ppapi_host.h" #include "ppapi/host/resource_host.h" @@ -100,7 +104,7 @@ std::unique_ptr<ppapi::host::ResourceHost> PepperRendererHostFactoryQt::CreateRe // We should either rename PPB_FlashFont_File to PPB_FontFile_Private or get // rid of its use in PDF if possible. if (host_->GetPpapiHost()->permissions().HasPermission(ppapi::PERMISSION_FLASH) - || host_->GetPpapiHost()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE)) { + || host_->GetPpapiHost()->permissions().HasPermission(ppapi::PERMISSION_PDF)) { switch (message.type()) { case PpapiHostMsg_FlashFontFile_Create::ID: { ppapi::proxy::SerializedFontDescription description; @@ -115,14 +119,14 @@ std::unique_ptr<ppapi::host::ResourceHost> PepperRendererHostFactoryQt::CreateRe } } - if (host_->GetPpapiHost()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE)) { +#if QT_CONFIG(webengine_printing_and_pdf) + if (host_->GetPpapiHost()->permissions().HasPermission(ppapi::PERMISSION_PDF)) { switch (message.type()) { case PpapiHostMsg_PDF_Create::ID: - // Not implemented - break; + return std::make_unique<pdf::PepperPDFHost>(host_, instance, resource); } } - +#endif // QT_CONFIG(webengine_printing_and_pdf) return nullptr; } diff --git a/src/core/renderer/print_web_view_helper_delegate_qt.cpp b/src/core/renderer/print_web_view_helper_delegate_qt.cpp index e693cf096..67cdd6b66 100644 --- a/src/core/renderer/print_web_view_helper_delegate_qt.cpp +++ b/src/core/renderer/print_web_view_helper_delegate_qt.cpp @@ -41,8 +41,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE.Chromium file. -#include "print_web_view_helper_delegate_qt.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_view.h" +#include "extensions/common/constants.h" +#include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_element.h" +#include "third_party/blink/public/web/web_local_frame.h" + +#include "print_web_view_helper_delegate_qt.h" #include "web_engine_library_info.h" namespace QtWebEngineCore { @@ -58,6 +64,15 @@ bool PrintWebViewHelperDelegateQt::CancelPrerender(content::RenderFrame *) blink::WebElement PrintWebViewHelperDelegateQt::GetPdfElement(blink::WebLocalFrame* frame) { + GURL url = frame->GetDocument().Url(); + if (url.SchemeIs(extensions::kExtensionScheme) && url.host() == extension_misc::kPdfExtensionId) + { + // <object> with id="plugin" is created in + // chrome/browser/resources/pdf/pdf.js. + auto plugin_element = frame->GetDocument().GetElementById("plugin"); + CHECK(!plugin_element.IsNull()); + return plugin_element; + } return blink::WebElement(); } diff --git a/src/core/renderer/render_frame_observer_qt.h b/src/core/renderer/render_frame_observer_qt.h index bca45bd8e..4c05422bb 100644 --- a/src/core/renderer/render_frame_observer_qt.h +++ b/src/core/renderer/render_frame_observer_qt.h @@ -44,6 +44,8 @@ #include "base/compiler_specific.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_frame_observer_tracker.h" +#include "ppapi/buildflags/buildflags.h" +#include "services/service_manager/public/cpp/binder_registry.h" namespace content { class RenderFrame; @@ -67,10 +69,13 @@ public: bool isFrameDetached() const; + service_manager::BinderRegistry* registry() { return ®istry_; } + private: DISALLOW_COPY_AND_ASSIGN(RenderFrameObserverQt); bool m_isFrameDetached; + service_manager::BinderRegistry registry_; }; } // namespace QtWebEngineCore diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp index 920fda72e..46f5de2c2 100644 --- a/src/core/renderer/user_resource_controller.cpp +++ b/src/core/renderer/user_resource_controller.cpp @@ -103,7 +103,7 @@ static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url) matchFound = false; for (auto it = scriptData.urlPatterns.begin(), end = scriptData.urlPatterns.end(); it != end; ++it) { URLPattern urlPattern(validUserScriptSchemes()); - if (urlPattern.Parse(*it) == URLPattern::PARSE_SUCCESS && urlPattern.MatchesURL(url)) + if (urlPattern.Parse(*it) == URLPattern::ParseResult::kSuccess && urlPattern.MatchesURL(url)) matchFound = true; } if (!matchFound) @@ -137,7 +137,7 @@ public: private: // RenderFrameObserver implementation. - void DidCommitProvisionalLoad(bool is_new_navigation, bool is_same_document_navigation) override; + void DidCommitProvisionalLoad(bool is_same_document_navigation, ui::PageTransition transition) override; void DidClearWindowObject() override; void DidFinishDocumentLoad() override; void DidFinishLoad() override; @@ -230,8 +230,8 @@ UserResourceController::RenderViewObserverHelper::RenderViewObserverHelper(conte { } -void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad(bool /* is_new_navigation */, - bool is_same_document_navigation) +void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition /*transitionbool*/) { if (is_same_document_navigation) return; diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp index 1f496e810..f1da8289a 100644 --- a/src/core/renderer/web_channel_ipc_transport.cpp +++ b/src/core/renderer/web_channel_ipc_transport.cpp @@ -137,6 +137,8 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) args->ThrowTypeError("Missing argument"); return; } + v8::Isolate *isolate = blink::MainThreadIsolate(); + v8::HandleScope handleScope(isolate); if (!jsonValue->IsString()) { args->ThrowTypeError("Expected string"); @@ -144,10 +146,10 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) } v8::Local<v8::String> jsonString = v8::Local<v8::String>::Cast(jsonValue); - QByteArray json(jsonString->Utf8Length(), 0); - jsonString->WriteUtf8(json.data(), json.size(), - nullptr, - v8::String::REPLACE_INVALID_UTF8); + QByteArray json(jsonString->Utf8Length(isolate), 0); + jsonString->WriteUtf8(isolate, + json.data(), json.size(), + nullptr, v8::String::REPLACE_INVALID_UTF8); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(json, &error); diff --git a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp index 2c8b1246a..5d7c3973f 100644 --- a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp +++ b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp @@ -45,8 +45,10 @@ #include "pepper_isolated_file_system_message_filter.h" #include "base/macros.h" +#include "base/task/post_task.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/render_view_host.h" @@ -83,7 +85,7 @@ scoped_refptr<base::TaskRunner> PepperIsolatedFileSystemMessageFilter::OverrideT { // In order to reach ExtensionSystem, we need to get ProfileManager first. // ProfileManager lives in UI thread, so we need to do this in UI thread. - return content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI); + return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); } int32_t PepperIsolatedFileSystemMessageFilter::OnResourceMessageReceived(const IPC::Message& msg, ppapi::host::HostMessageContext *context) diff --git a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp new file mode 100644 index 000000000..0a6adf4d6 --- /dev/null +++ b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE.Chromium file. + +#include "resource_dispatcher_host_delegate_qt.h" + +#include "base/bind.h" +#include "base/guid.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/download_request_utils.h" +#include "content/public/browser/navigation_controller.h" + +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/browser/stream_info.h" +#include "content/public/browser/web_contents.h" + +#include "extensions/extension_system_qt.h" +#include "extensions/browser/info_map.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" + +#include "net/base/escape.h" +#include "net/url_request/url_request.h" + +#include "resource_context_qt.h" +#include "type_conversion.h" +#include "web_contents_delegate_qt.h" +#include "web_engine_settings.h" + +namespace QtWebEngineCore { + +void OnPdfStreamIntercepted( + const GURL& original_url, + std::string extension_id, + int frame_tree_node_id, + const content::ResourceRequestInfo::WebContentsGetter& + web_contents_getter) { + content::WebContents* web_contents = web_contents_getter.Run(); + if (!web_contents) + return; + + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt*>(web_contents->GetDelegate()); + if (!contentsDelegate) + return; + + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (!settings->testAttribute(WebEngineSettings::PDFViewerEnabled) + || !settings->testAttribute(WebEngineSettings::PluginsEnabled)) { + // If the applications has been set up to always download PDF files to open them in an + // external viewer, trigger the download. + std::unique_ptr<download::DownloadUrlParameters> params( + content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( + web_contents, original_url, NO_TRAFFIC_ANNOTATION_YET)); + content::BrowserContext::GetDownloadManager(web_contents->GetBrowserContext()) + ->DownloadUrl(std::move(params)); + return; + } + + // The URL passes the original pdf resource url, that will be requested + // by the pdf viewer extension page. + content::NavigationController::LoadURLParams params( + GURL(base::StringPrintf("%s://%s/index.html?%s", extensions::kExtensionScheme, + extension_id.c_str(), + net::EscapeUrlEncodedData(original_url.spec(), false).c_str()))); + + params.frame_tree_node_id = frame_tree_node_id; + web_contents->GetController().LoadURLWithParams(params); +} + +bool ResourceDispatcherHostDelegateQt::ShouldInterceptResourceAsStream(net::URLRequest *request, + const std::string &mime_type, + GURL *origin, + std::string *payload) +{ + const content::ResourceRequestInfo* info = + content::ResourceRequestInfo::ForRequest(request); + + int render_process_host_id = -1; + int render_frame_id = -1; + if (!content::ResourceRequestInfo::GetRenderFrameForRequest(request, &render_process_host_id, &render_frame_id)) + return false; + + ResourceContextQt *context = static_cast<ResourceContextQt *>(info->GetContext()); + std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist(); + + extensions::ExtensionSystemQt *extensionSystem = context->GetExtensionSystem(); + if (!extensionSystem) + return false; + + const scoped_refptr<const extensions::InfoMap> extension_info_map(extensionSystem->info_map()); + + for (const std::string &extension_id : whitelist) { + const extensions::Extension *extension = extension_info_map->extensions().GetByID(extension_id); + if (!extension) + continue; + + MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension); + if (!handler) + continue; + if (handler->CanHandleMIMEType(mime_type)) { + StreamTargetInfo target_info; + *origin = extensions::Extension::GetBaseURLFromExtensionId(extension_id); + target_info.extension_id = extension_id; + target_info.view_id = base::GenerateGUID(); + *payload = target_info.view_id; + stream_target_info_[request] = target_info; + return true; + } + } + return false; +} + +// Informs the delegate that a Stream was created. The Stream can be read from +// the blob URL of the Stream, but can only be read once. +void ResourceDispatcherHostDelegateQt::OnStreamCreated(net::URLRequest *request, + std::unique_ptr<content::StreamInfo> stream) +{ + const content::ResourceRequestInfo *info = content::ResourceRequestInfo::ForRequest(request); + std::map<net::URLRequest *, StreamTargetInfo>::iterator ix = stream_target_info_.find(request); + CHECK(ix != stream_target_info_.end()); + int render_frame_id = -1; + int render_process_id = -1; + if (!content::ResourceRequestInfo::GetRenderFrameForRequest(request, &render_process_id, &render_frame_id)) { + stream_target_info_.erase(request); + request->Cancel(); + return; + } + + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&OnPdfStreamIntercepted, + request->url(), ix->second.extension_id, + info->GetFrameTreeNodeId(), info->GetWebContentsGetterForRequest() + ) + ); + stream_target_info_.erase(request); +} + +} // namespace QtWebEngineCore diff --git a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h new file mode 100644 index 000000000..3039fd03e --- /dev/null +++ b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H +#define RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H + +#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "extensions/buildflags/buildflags.h" + +#include "web_contents_adapter_client.h" + +namespace QtWebEngineCore { + +class ResourceDispatcherHostDelegateQt : public content::ResourceDispatcherHostDelegate { +public: + // If the stream will be rendered in a BrowserPlugin, |payload| will contain + // the data that should be given to the old ResourceHandler to forward to the + // renderer process. + bool ShouldInterceptResourceAsStream(net::URLRequest *request, + const std::string &mime_type, + GURL *origin, + std::string *payload) override; + + // Informs the delegate that a Stream was created. The Stream can be read from + // the blob URL of the Stream, but can only be read once. + void OnStreamCreated(net::URLRequest *request, + std::unique_ptr<content::StreamInfo> stream) override; +private: +#if BUILDFLAG(ENABLE_EXTENSIONS) + struct StreamTargetInfo { + std::string extension_id; + std::string view_id; + }; + std::map<net::URLRequest *, StreamTargetInfo> stream_target_info_; +#endif + +}; + +} // namespace QtWebEngineCore + +#endif // RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H diff --git a/src/core/resource_bundle_qt.cpp b/src/core/resource_bundle_qt.cpp index 428faa34e..dc7507f34 100644 --- a/src/core/resource_bundle_qt.cpp +++ b/src/core/resource_bundle_qt.cpp @@ -71,6 +71,7 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) return GetEmptyImage(); } +// static bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { #if defined(OS_LINUX) diff --git a/src/core/resource_context_qt.cpp b/src/core/resource_context_qt.cpp index 6dfa5064e..78be72107 100644 --- a/src/core/resource_context_qt.cpp +++ b/src/core/resource_context_qt.cpp @@ -64,4 +64,11 @@ net::URLRequestContext* ResourceContextQt::GetRequestContext() return m_io_data->urlRequestContext(); } +#if BUILDFLAG(ENABLE_EXTENSIONS) +extensions::ExtensionSystemQt* ResourceContextQt::GetExtensionSystem() +{ + return m_io_data->GetExtensionSystem(); +} +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + } // namespace QtWebEngineCore diff --git a/src/core/resource_context_qt.h b/src/core/resource_context_qt.h index 6a5e7a74e..ccbe2c364 100644 --- a/src/core/resource_context_qt.h +++ b/src/core/resource_context_qt.h @@ -42,6 +42,20 @@ #include "content/public/browser/resource_context.h" +#include "extensions/buildflags/buildflags.h" + +namespace net { +class URLRequestContextGetter; +} + +#if BUILDFLAG(ENABLE_EXTENSIONS) +namespace extensions { +class ExtensionSystemQt; +} +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + +class GURL; + namespace QtWebEngineCore { class ProfileIODataQt; @@ -51,7 +65,11 @@ class ResourceContextQt : public content::ResourceContext public: ResourceContextQt(ProfileIODataQt *io_data); net::URLRequestContext *GetRequestContext() override; +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionSystemQt* GetExtensionSystem(); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) private: + friend class ProfileIODataQt; ProfileIODataQt* m_io_data; DISALLOW_COPY_AND_ASSIGN(ResourceContextQt); }; diff --git a/src/core/service/service_qt.cpp b/src/core/service/service_qt.cpp index 30ed269e8..bb842232c 100644 --- a/src/core/service/service_qt.cpp +++ b/src/core/service/service_qt.cpp @@ -45,7 +45,9 @@ #include "service_qt.h" #include "base/no_destructor.h" +#include "base/task/post_task.h" #include "components/spellcheck/spellcheck_buildflags.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "services/service_manager/public/cpp/binder_registry.h" @@ -82,10 +84,10 @@ private: ServiceQt::IOThreadContext::IOThreadContext() { - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI); #if BUILDFLAG(ENABLE_SPELLCHECK) - m_registry_with_source_info.AddInterface(base::Bind(&SpellCheckHostChromeImpl::Create), ui_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); + m_registry_with_source_info.AddInterface(base::BindRepeating(&SpellCheckHostChromeImpl::Create), ui_task_runner); #endif } @@ -97,7 +99,7 @@ void ServiceQt::IOThreadContext::BindConnector(service_manager::mojom::Connector // on the IO thread. Post a task instead. As long as this task is posted // before any code attempts to connect to the chrome service, there's no // race. - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)->PostTask( + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})->PostTask( FROM_HERE, base::BindOnce(&IOThreadContext::BindConnectorOnIOThread, base::Unretained(this), diff --git a/src/core/touch_handle_drawable_client.h b/src/core/touch_handle_drawable_client.h new file mode 100644 index 000000000..42d907d75 --- /dev/null +++ b/src/core/touch_handle_drawable_client.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_HANDLE_DRAWABLE_CLIENT_H +#define TOUCH_HANDLE_DRAWABLE_CLIENT_H + +#include "qtwebenginecoreglobal_p.h" +#include <QRect> + +namespace QtWebEngineCore { + +class QWEBENGINECORE_PRIVATE_EXPORT TouchHandleDrawableClient { +public: + virtual ~TouchHandleDrawableClient() { } + + virtual void setImage(int orientation) = 0; + virtual void setBounds(const QRect &bounds) = 0; + virtual void setVisible(bool visible) = 0; + virtual void setOpacity(float opacity) = 0; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_HANDLE_DRAWABLE_CLIENT_H diff --git a/src/core/touch_handle_drawable_qt.cpp b/src/core/touch_handle_drawable_qt.cpp new file mode 100644 index 000000000..66b1cf40e --- /dev/null +++ b/src/core/touch_handle_drawable_qt.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE.Chromium file. + +// This implementation is based on chromium/ui/touch_selection/touch_handle_drawable_aura.cc + +#include "render_widget_host_view_qt.h" +#include "touch_handle_drawable_client.h" +#include "touch_handle_drawable_qt.h" +#include "type_conversion.h" +#include "web_contents_adapter_client.h" + +#include "ui/gfx/image/image.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/resources/grit/ui_resources.h" + +namespace QtWebEngineCore { + +namespace { +// The distance by which a handle image is offset from the focal point (i.e. +// text baseline) downwards. +const int kSelectionHandleVerticalVisualOffset = 2; + +// The padding around the selection handle image can be used to extend the +// handle window so that touch events near the selection handle image are +// targeted to the selection handle window. +const int kSelectionHandlePadding = 0; + +// Epsilon value used to compare float values to zero. +const float kEpsilon = 1e-8f; + +// Returns the appropriate handle image based on the handle orientation. +gfx::Image* GetHandleImage(ui::TouchHandleOrientation orientation) +{ + int resource_id = 0; + switch (orientation) { + case ui::TouchHandleOrientation::LEFT: + resource_id = IDR_TEXT_SELECTION_HANDLE_LEFT; + break; + case ui::TouchHandleOrientation::CENTER: + resource_id = IDR_TEXT_SELECTION_HANDLE_CENTER; + break; + case ui::TouchHandleOrientation::RIGHT: + resource_id = IDR_TEXT_SELECTION_HANDLE_RIGHT; + break; + case ui::TouchHandleOrientation::UNDEFINED: + NOTREACHED() << "Invalid touch handle bound type."; + return nullptr; + }; + return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(resource_id); +} + +bool IsNearlyZero(float value) +{ + return std::abs(value) < kEpsilon; +} + +} // namespace + +TouchHandleDrawableQt::TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_enabled(false) + , m_alpha(0) + , m_orientation(ui::TouchHandleOrientation::UNDEFINED) +{ + QMap<int, QImage> images; + for (int orientation = 0; orientation < static_cast<int>(ui::TouchHandleOrientation::UNDEFINED); ++orientation) { + gfx::Image* image = GetHandleImage(static_cast<ui::TouchHandleOrientation>(orientation)); + images.insert(orientation, toQImage(image->AsBitmap())); + } + + Q_ASSERT(m_rwhv); + Q_ASSERT(m_rwhv->adapterClient()); + m_client.reset(m_rwhv->adapterClient()->createTouchHandle(images)); +} + +TouchHandleDrawableQt::~TouchHandleDrawableQt() +{ +} + +void TouchHandleDrawableQt::UpdateBounds() +{ + if (!m_client) + return; + + gfx::RectF newBounds = m_relativeBounds; + newBounds.Offset(m_originPosition.x(), m_originPosition.y()); + m_client->setBounds(toQt(gfx::ToEnclosingRect(newBounds))); +} + +bool TouchHandleDrawableQt::IsVisible() const +{ + return m_enabled && !IsNearlyZero(m_alpha); +} + +void TouchHandleDrawableQt::SetEnabled(bool enabled) +{ + if (!m_client) + return; + + if (enabled == m_enabled) + return; + + m_enabled = enabled; + m_client->setVisible(enabled); +} + +void TouchHandleDrawableQt::SetOrientation(ui::TouchHandleOrientation orientation, bool mirror_vertical, bool mirror_horizontal) +{ + if (!m_client) + return; + + // TODO: Implement adaptive handle orientation logic + DCHECK(!mirror_vertical); + DCHECK(!mirror_horizontal); + + if (m_orientation == orientation) + return; + m_orientation = orientation; + gfx::Image* image = GetHandleImage(orientation); + m_client->setImage(static_cast<int>(orientation)); + + // Calculate the relative bounds. + gfx::Size image_size = image->Size(); + int window_width = image_size.width() + 2 * kSelectionHandlePadding; + int window_height = image_size.height() + 2 * kSelectionHandlePadding; + m_relativeBounds = + gfx::RectF(-kSelectionHandlePadding, + kSelectionHandleVerticalVisualOffset - kSelectionHandlePadding, + window_width, window_height); + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetOrigin(const gfx::PointF& position) +{ + m_originPosition = position; + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetAlpha(float alpha) +{ + if (!m_client) + return; + + if (alpha == m_alpha) + return; + + m_alpha = alpha; + m_client->setOpacity(m_alpha); + m_client->setVisible(IsVisible()); +} + +gfx::RectF TouchHandleDrawableQt::GetVisibleBounds() const +{ + gfx::RectF bounds = m_relativeBounds; + bounds.Offset(m_originPosition.x(), m_originPosition.y()); + + gfx::RectF visibleBounds(bounds); + visibleBounds.Inset(kSelectionHandlePadding, + kSelectionHandlePadding + kSelectionHandleVerticalVisualOffset, + kSelectionHandlePadding, + kSelectionHandlePadding); + return visibleBounds; +} + +float TouchHandleDrawableQt::GetDrawableHorizontalPaddingRatio() const +{ + // Qt does not have any transparent padding for its handle drawable. + return 0.0; +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_handle_drawable_qt.h b/src/core/touch_handle_drawable_qt.h new file mode 100644 index 000000000..46fa217b7 --- /dev/null +++ b/src/core/touch_handle_drawable_qt.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_HANDLE_DRAWABLE_QT_H +#define TOUCH_HANDLE_DRAWABLE_QT_H + +#include "ui/touch_selection/touch_handle.h" +#include "ui/touch_selection/touch_handle_orientation.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchHandleDrawableClient; + +class TouchHandleDrawableQt : public ui::TouchHandleDrawable +{ +public: + explicit TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv); + ~TouchHandleDrawableQt() override; + +private: + void UpdateBounds(); + bool IsVisible() const; + + // ui::TouchHandleDrawable overrides + void SetEnabled(bool enabled) override; + void SetOrientation(ui::TouchHandleOrientation orientation, + bool mirror_vertical, + bool mirror_horizontal) override; + void SetOrigin(const gfx::PointF& position) override; + void SetAlpha(float alpha) override; + gfx::RectF GetVisibleBounds() const override; + float GetDrawableHorizontalPaddingRatio() const override; + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchHandleDrawableClient> m_client; + + bool m_enabled; + float m_alpha; + ui::TouchHandleOrientation m_orientation; + gfx::RectF m_relativeBounds; + gfx::PointF m_originPosition; + + DISALLOW_COPY_AND_ASSIGN(TouchHandleDrawableQt); +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_HANDLE_DRAWABLE_QT_H diff --git a/src/core/touch_selection_controller_client_qt.cpp b/src/core/touch_selection_controller_client_qt.cpp new file mode 100644 index 000000000..da3c78b8a --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "render_widget_host_view_qt.h" +#include "touch_handle_drawable_qt.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" +#include "type_conversion.h" +#include "web_contents_adapter.h" +#include "web_contents_adapter_client.h" + +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "ui/gfx/geometry/size_conversions.h" + +#include <QClipboard> +#include <QGuiApplication> + +namespace QtWebEngineCore { + +TouchSelectionControllerClientQt::TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_menuController(new TouchSelectionMenuController(this)) + , m_menuShowing(false) + , m_menuRequested(false) + , m_touchDown(false) + , m_scrollInProgress(false) + , m_handleDragInProgress(false) +{ + Q_ASSERT(rwhv); +} + +TouchSelectionControllerClientQt::~TouchSelectionControllerClientQt() +{ +} + +bool TouchSelectionControllerClientQt::handleContextMenu(const content::ContextMenuParams& params) +{ + if ((params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP) && + params.is_editable && params.selection_text.empty()) { + m_menuRequested = true; + updateMenu(); + return true; + } + + const bool from_touch = params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP || + params.source_type == ui::MENU_SOURCE_TOUCH; + if (from_touch && !params.selection_text.empty()) + return true; + + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + return false; +} + +void TouchSelectionControllerClientQt::onTouchDown() +{ + m_touchDown = true; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onTouchUp() +{ + m_touchDown = false; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollBegin() +{ + m_scrollInProgress = true; + GetTouchSelectionController()->SetTemporarilyHidden(true); + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollEnd() +{ + m_scrollInProgress = false; + GetTouchSelectionController()->SetTemporarilyHidden(false); + updateMenu(); +} + +bool TouchSelectionControllerClientQt::IsCommandIdEnabled(int command_id) const +{ + bool editable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_NONE; + bool readable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD; + bool hasSelection = !m_rwhv->GetSelectedText().empty(); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + return editable && readable && hasSelection; + case TouchSelectionMenuController::Copy: + return readable && hasSelection; + case TouchSelectionMenuController::Paste: + return editable && !QGuiApplication::clipboard()->text().isEmpty(); + default: + return false; + } +} + +void TouchSelectionControllerClientQt::ExecuteCommand(int command_id, int event_flags) +{ + Q_UNUSED(event_flags); + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + WebContentsAdapter *adapter = adapterClient->webContentsAdapter(); + Q_ASSERT(adapter); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + adapter->cut(); + break; + case TouchSelectionMenuController::Copy: + adapter->copy(); + break; + case TouchSelectionMenuController::Paste: + adapter->paste(); + break; + default: + NOTREACHED(); + break; + } +} + +void TouchSelectionControllerClientQt::RunContextMenu() +{ + gfx::RectF anchorRect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF anchorPoint = gfx::PointF(anchorRect.CenterPoint().x(), anchorRect.y()); + + content::RenderWidgetHostImpl *host = m_rwhv->host(); + host->ShowContextMenuAtPoint(gfx::ToRoundedPoint(anchorPoint), + ui::MENU_SOURCE_TOUCH_EDIT_MENU); + + // Hide selection handles after getting rect-between-bounds from touch + // selection controller; otherwise, rect would be empty and the above + // calculations would be invalid. + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); +} + +void TouchSelectionControllerClientQt::DidStopFlinging() +{ + onScrollEnd(); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end) +{ + UpdateClientSelectionBounds(start, end, this, this); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) +{ + Q_UNUSED(client); + Q_UNUSED(menu_client); + + GetTouchSelectionController()->OnSelectionBoundsChanged(start, end); +} + +void TouchSelectionControllerClientQt::InvalidateClient(ui::TouchSelectionControllerClient* client) +{ + Q_UNUSED(client); +} + +ui::TouchSelectionController* TouchSelectionControllerClientQt::GetTouchSelectionController() +{ + return m_rwhv->getTouchSelectionController(); +} + +void TouchSelectionControllerClientQt::AddObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +void TouchSelectionControllerClientQt::RemoveObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +bool TouchSelectionControllerClientQt::SupportsAnimation() const +{ + return false; +} + +void TouchSelectionControllerClientQt::SetNeedsAnimate() +{ + NOTREACHED(); +} + +void TouchSelectionControllerClientQt::MoveCaret(const gfx::PointF& position) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveCaret(gfx::ToRoundedPoint(position)); +} + +void TouchSelectionControllerClientQt::MoveRangeSelectionExtent(const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->SelectRange(gfx::ToRoundedPoint(base), gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::OnSelectionEvent(ui::SelectionEventType event) +{ + switch (event) { + case ui::SELECTION_HANDLES_SHOWN: + m_menuRequested = true; + break; + case ui::INSERTION_HANDLE_SHOWN: + break; + case ui::SELECTION_HANDLES_CLEARED: + case ui::INSERTION_HANDLE_CLEARED: + m_menuRequested = false; + break; + case ui::SELECTION_HANDLE_DRAG_STARTED: + case ui::INSERTION_HANDLE_DRAG_STARTED: + m_handleDragInProgress = true; + break; + case ui::SELECTION_HANDLE_DRAG_STOPPED: + case ui::INSERTION_HANDLE_DRAG_STOPPED: + m_handleDragInProgress = false; + break; + case ui::SELECTION_HANDLES_MOVED: + case ui::INSERTION_HANDLE_MOVED: + break; + case ui::INSERTION_HANDLE_TAPPED: + m_menuRequested = !m_menuRequested; + break; + } + + updateMenu(); +} + +void TouchSelectionControllerClientQt::OnDragUpdate(const gfx::PointF& position) +{ + Q_UNUSED(position); +} + +std::unique_ptr<ui::TouchHandleDrawable> TouchSelectionControllerClientQt::CreateDrawable() +{ + return std::unique_ptr<ui::TouchHandleDrawable>(new TouchHandleDrawableQt(m_rwhv)); +} + +void TouchSelectionControllerClientQt::DidScroll() +{ +} + +void TouchSelectionControllerClientQt::showMenu() +{ + gfx::RectF rect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF origin = rect.origin(); + gfx::PointF bottom_right = rect.bottom_right(); + + gfx::Vector2dF diagonal = bottom_right - origin; + gfx::SizeF size(diagonal.x(), diagonal.y()); + gfx::RectF anchor_rect(origin, size); + + // Calculate maximum handle image size; + gfx::SizeF max_handle_size = GetTouchSelectionController()->GetStartHandleRect().size(); + max_handle_size.SetToMax(GetTouchSelectionController()->GetEndHandleRect().size()); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->showTouchSelectionMenu(m_menuController.get(), + QRect(toQt(gfx::ToEnclosingRect(anchor_rect))), + QSize(toQt(gfx::ToRoundedSize(max_handle_size)))); + m_menuShowing = true; +} + +void TouchSelectionControllerClientQt::hideMenu() +{ + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->hideTouchSelectionMenu(); + m_menuShowing = false; +} + +void TouchSelectionControllerClientQt::updateMenu() +{ + if (m_menuShowing) + hideMenu(); + + if (m_menuRequested && !m_touchDown && + !m_scrollInProgress && !m_handleDragInProgress) { + showMenu(); + } +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_controller_client_qt.h b/src/core/touch_selection_controller_client_qt.h new file mode 100644 index 000000000..cdc45cac3 --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H +#define TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H + +#include "content/public/browser/touch_selection_controller_client_manager.h" +#include "content/public/common/context_menu_params.h" +#include "ui/touch_selection/touch_selection_controller.h" +#include "ui/touch_selection/touch_selection_menu_runner.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchSelectionMenuController; + +class TouchSelectionControllerClientQt + : public ui::TouchSelectionControllerClient + , public ui::TouchSelectionMenuClient + , public content::TouchSelectionControllerClientManager +{ +public: + explicit TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv); + ~TouchSelectionControllerClientQt() override; + + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end); + bool handleContextMenu(const content::ContextMenuParams& params); + void onTouchDown(); + void onTouchUp(); + void onScrollBegin(); + void onScrollEnd(); + + // ui::TouchSelectionMenuClient overrides + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + void RunContextMenu() override; + + // content::TouchSelectionControllerClientManager overrides + void DidStopFlinging() override; + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) override; + void InvalidateClient(ui::TouchSelectionControllerClient* client) override; + ui::TouchSelectionController* GetTouchSelectionController() override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + + // ui::TouchSelectionControllerClient overrides + bool SupportsAnimation() const override; + void SetNeedsAnimate() override; + void MoveCaret(const gfx::PointF& position) override; + void MoveRangeSelectionExtent(const gfx::PointF& extent) override; + void SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) override; + void OnSelectionEvent(ui::SelectionEventType event) override; + void OnDragUpdate(const gfx::PointF& position) override; + std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override; + void DidScroll() override; + +private: + void showMenu(); + void hideMenu(); + void updateMenu(); + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchSelectionMenuController> m_menuController; + + bool m_menuShowing; + bool m_menuRequested; + bool m_touchDown; + bool m_scrollInProgress; + bool m_handleDragInProgress; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/touch_selection_menu_controller.cpp b/src/core/touch_selection_menu_controller.cpp new file mode 100644 index 000000000..cdec9a064 --- /dev/null +++ b/src/core/touch_selection_menu_controller.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" + +namespace QtWebEngineCore { + +TouchSelectionMenuController::TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient) + : m_touchSelectionControllerClient(touchSelectionControllerClient) +{ +} + +TouchSelectionMenuController::~TouchSelectionMenuController() +{ +} + +int TouchSelectionMenuController::buttonCount() +{ + int buttonCount = 1; + + for (int commandId = 0; commandId <= static_cast<int>(Paste); ++commandId) { + if (m_touchSelectionControllerClient->IsCommandIdEnabled(commandId)) + buttonCount++; + } + + return buttonCount; +} + +bool TouchSelectionMenuController::isCommandEnabled(TouchSelectionCommand command) +{ + return m_touchSelectionControllerClient->IsCommandIdEnabled(static_cast<int>(command)); +} + +void TouchSelectionMenuController::cut() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Cut), 0); +} + +void TouchSelectionMenuController::copy() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Copy), 0); +} + +void TouchSelectionMenuController::paste() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Paste), 0); +} + +void TouchSelectionMenuController::runContextMenu() +{ + return m_touchSelectionControllerClient->RunContextMenu(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_menu_controller.h b/src/core/touch_selection_menu_controller.h new file mode 100644 index 000000000..fd61ae709 --- /dev/null +++ b/src/core/touch_selection_menu_controller.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_MENU_CONTROLLER_H +#define TOUCH_SELECTION_MENU_CONTROLLER_H + +#include "qtwebenginecoreglobal_p.h" +#include <QtCore/QObject> + +namespace QtWebEngineCore { + +class TouchSelectionControllerClientQt; + +class QWEBENGINECORE_PRIVATE_EXPORT TouchSelectionMenuController : public QObject { + Q_OBJECT +public: + enum TouchSelectionCommand { + Cut, + Copy, + Paste + }; + + TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient); + ~TouchSelectionMenuController(); + int buttonCount(); + bool isCommandEnabled(TouchSelectionCommand); + +public Q_SLOTS: + void cut(); + void copy(); + void paste(); + void runContextMenu(); + +private: + TouchSelectionControllerClientQt *m_touchSelectionControllerClient; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp index f69b2a297..02d2db448 100644 --- a/src/core/type_conversion.cpp +++ b/src/core/type_conversion.cpp @@ -42,7 +42,9 @@ #include <content/public/common/favicon_url.h> #include <ui/events/event_constants.h> #include <ui/gfx/image/image_skia.h> + #include <QtCore/qcoreapplication.h> +#include <QtGui/qmatrix4x4.h> namespace QtWebEngineCore { @@ -131,7 +133,7 @@ QImage toQImage(const SkBitmap &bitmap) QImage toQImage(const gfx::ImageSkiaRep &imageSkiaRep) { - QImage image = toQImage(imageSkiaRep.sk_bitmap()); + QImage image = toQImage(imageSkiaRep.GetBitmap()); if (!image.isNull() && imageSkiaRep.scale() != 1.0f) image.setDevicePixelRatio(imageSkiaRep.scale()); return image; @@ -243,4 +245,15 @@ FaviconInfo toFaviconInfo(const content::FaviconURL &favicon_url) return info; } +void convertToQt(const SkMatrix44 &m, QMatrix4x4 &c) +{ + QMatrix4x4 qtMatrix( + m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), + m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), + m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), + m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3)); + qtMatrix.optimize(); + c = qtMatrix; +} + } // namespace QtWebEngineCore diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index afc3c3336..7b1f1b4d6 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -45,7 +45,6 @@ #include <QDir> #include <QIcon> #include <QImage> -#include <QMatrix4x4> #include <QNetworkCookie> #include <QRect> #include <QString> @@ -64,6 +63,8 @@ #include "ui/gfx/geometry/rect_f.h" #include "url/gurl.h" +QT_FORWARD_DECLARE_CLASS(QMatrix4x4) + namespace content { struct FaviconURL; } @@ -171,6 +172,11 @@ inline gfx::SizeF toGfx(const QSizeF& size) return gfx::SizeF(size.width(), size.height()); } +inline gfx::Rect toGfx(const QRect &rect) +{ + return gfx::Rect(rect.x(), rect.y(), rect.width(), rect.height()); +} + inline QSizeF toQt(const gfx::SizeF &size) { return QSizeF(size.width(), size.height()); @@ -198,16 +204,7 @@ SkBitmap toSkBitmap(const QImage &image); QIcon toQIcon(const std::vector<SkBitmap> &bitmaps); -inline QMatrix4x4 toQt(const SkMatrix44 &m) -{ - QMatrix4x4 qtMatrix( - m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), - m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), - m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), - m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3)); - qtMatrix.optimize(); - return qtMatrix; -} +void convertToQt(const SkMatrix44 &m, QMatrix4x4 &c); inline QDateTime toQt(base::Time time) { diff --git a/src/core/user_notification_controller.cpp b/src/core/user_notification_controller.cpp new file mode 100644 index 000000000..82cb57e51 --- /dev/null +++ b/src/core/user_notification_controller.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "user_notification_controller.h" + +#include "type_conversion.h" + +#include "base/callback.h" +#include "content/public/browser/notification_event_dispatcher.h" +#include "third_party/blink/public/common/notifications/notification_resources.h" +#include "third_party/blink/public/common/notifications/platform_notification_data.h" +#include "ui/message_center/public/cpp/notification_delegate.h" + +#include <memory> + +namespace QtWebEngineCore { + +static Qt::LayoutDirection toDirection(blink::PlatformNotificationData::Direction direction) +{ + switch (direction) { + case blink::PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT: + return Qt::LeftToRight; + case blink::PlatformNotificationData::DIRECTION_RIGHT_TO_LEFT: + return Qt::RightToLeft; + case blink::PlatformNotificationData::DIRECTION_AUTO: + default: + break; + } + return Qt::LayoutDirectionAuto; +} + +class UserNotificationControllerPrivate { +public: + UserNotificationControllerPrivate(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin) + : m_params(params) + , m_origin(origin) + , m_delegate(nullptr) + , m_resources(resources) + , m_client(nullptr) + , m_iconGenerated(false) + , m_imageGenerated(false) + , m_badgeGenerated(false) + , m_shown(false) + { } + + blink::PlatformNotificationData m_params; + GURL m_origin; + std::unique_ptr<UserNotificationController::Delegate> m_delegate; + blink::NotificationResources m_resources; + UserNotificationController::Client *m_client; + QIcon m_icon; + QImage m_image; + QImage m_badge; + bool m_iconGenerated; + bool m_imageGenerated; + bool m_badgeGenerated; + bool m_shown; +}; + + +UserNotificationController::UserNotificationController(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin, + Delegate *delegate) + : d(new UserNotificationControllerPrivate(params, resources, origin)) +{ + d->m_delegate.reset(delegate); +} + +UserNotificationController::~UserNotificationController() +{ + delete d; + d = nullptr; +} + +void UserNotificationController::notificationDisplayed() +{ + if (!d->m_shown) { + d->m_shown = true; + if (d->m_delegate) + d->m_delegate->shown(); + } +} + +void UserNotificationController::notificationClosed() +{ + d->m_shown = false; + if (d->m_delegate) + d->m_delegate->closed(true); +} + +void UserNotificationController::notificationClicked() +{ + if (d->m_delegate) + d->m_delegate->clicked(); +} + +void UserNotificationController::closeNotification() +{ + d->m_shown = false; + if (d->m_client) + d->m_client->notificationClosed(this); +} + +void UserNotificationController::setClient(UserNotificationController::Client* client) +{ + d->m_client = client; +} + +UserNotificationController::Client* UserNotificationController::client() +{ + return d->m_client; +} + +QUrl UserNotificationController::origin() const +{ + return toQt(d->m_origin); +} + +QIcon UserNotificationController::icon() const +{ + if (!d->m_iconGenerated) { + d->m_iconGenerated = true; + if (!d->m_resources.notification_icon.isNull()) { + QImage image = toQImage(d->m_resources.notification_icon); + if (!image.isNull()) + d->m_icon = QIcon(QPixmap::fromImage(std::move(image), Qt::NoFormatConversion)); + } + } + return d->m_icon; +} + +QImage UserNotificationController::image() const +{ + if (d->m_imageGenerated) + return d->m_image; + d->m_image = toQImage(d->m_resources.image); + d->m_imageGenerated = true; + return d->m_image; +} + +QImage UserNotificationController::badge() const +{ + if (d->m_badgeGenerated) + return d->m_badge; + d->m_badge = toQImage(d->m_resources.badge); + d->m_badgeGenerated = true; + return d->m_badge; +} + +QString UserNotificationController::title() const +{ + return toQt(d->m_params.title); +} + +QString UserNotificationController::body() const +{ + return toQt(d->m_params.body); +} + +QString UserNotificationController::tag() const +{ + return toQt(d->m_params.tag); +} + +QString UserNotificationController::language() const +{ + return toQt(d->m_params.lang); +} + +Qt::LayoutDirection UserNotificationController::direction() const +{ + return toDirection(d->m_params.direction); +} + +bool UserNotificationController::isShown() const +{ + return d->m_shown; +} + +} // namespace QtWebEngineCore diff --git a/src/core/user_notification_controller.h b/src/core/user_notification_controller.h new file mode 100644 index 000000000..840074843 --- /dev/null +++ b/src/core/user_notification_controller.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESKTOP_NOTIFICATION_CONTROLLER_H +#define DESKTOP_NOTIFICATION_CONTROLLER_H + +#include "qtwebenginecoreglobal.h" + +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtGui/QIcon> + +class GURL; + +namespace blink { + struct NotificationResources; + struct PlatformNotificationData; +} + +namespace QtWebEngineCore { + +class UserNotificationControllerPrivate; + +// Works as an accessor and owner of chromium objects related to showing desktop notifications. +class QWEBENGINECORE_EXPORT UserNotificationController : public QEnableSharedFromThis<UserNotificationController> { +public: + struct Delegate { + virtual ~Delegate() { } + virtual void shown() = 0; + virtual void clicked() = 0; + virtual void closed(bool byUser) = 0; + }; + + UserNotificationController(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin, + Delegate *delegate); + ~UserNotificationController(); + + // The notification was shown. + void notificationDisplayed(); + + // The notification was closed. + void notificationClosed(); + + // The user clicked on the notification. + void notificationClicked(); + + // Chromium requests to close the notification. + void closeNotification(); + + QUrl origin() const; + QIcon icon() const; + QImage image() const; + QImage badge() const; + QString title() const; + QString body() const; + QString tag() const; + QString language() const; + Qt::LayoutDirection direction() const; + + bool isShown() const; + + class Client { + public: + virtual ~Client() { } + virtual void notificationClosed(const UserNotificationController *) = 0; + }; + void setClient(Client *client); + Client* client(); + +private: + UserNotificationControllerPrivate *d; +}; + +} // namespace QtWebEngineCore + +#endif // DESKTOP_NOTIFICATION_CONTROLLER_H diff --git a/src/core/visited_links_manager_qt.cpp b/src/core/visited_links_manager_qt.cpp index ac27446b8..d4885e8e8 100644 --- a/src/core/visited_links_manager_qt.cpp +++ b/src/core/visited_links_manager_qt.cpp @@ -39,7 +39,6 @@ #include "visited_links_manager_qt.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "profile_qt.h" #include "type_conversion.h" @@ -106,14 +105,13 @@ static void ensureDirectoryExists(const base::FilePath &path) errorstr.c_str()); } -VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileAdapter *adapter) +VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks) : m_delegate(new VisitedLinkDelegateQt) { - Q_ASSERT(adapter && adapter->profile()); - ProfileQt *profile = adapter->profile(); - if (adapter->persistVisitedLinks()) + Q_ASSERT(profile); + if (persistVisitedLinks) ensureDirectoryExists(profile->GetPath()); - m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), adapter->persistVisitedLinks())); + m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), persistVisitedLinks)); m_visitedLinkMaster->Init(); } diff --git a/src/core/visited_links_manager_qt.h b/src/core/visited_links_manager_qt.h index 8d9a7495b..5bbcc5983 100644 --- a/src/core/visited_links_manager_qt.h +++ b/src/core/visited_links_manager_qt.h @@ -67,14 +67,14 @@ class GURL; namespace QtWebEngineCore { -class ProfileAdapter; +class ProfileQt; class VisitedLinkDelegateQt; class QWEBENGINECORE_PRIVATE_EXPORT VisitedLinksManagerQt { public: virtual~VisitedLinksManagerQt(); - VisitedLinksManagerQt(ProfileAdapter *profileAdapter); + VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks); void deleteAllVisitedLinkData(); void deleteVisitedLinkDataForUrls(const QList<QUrl> &); diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 21540f5da..607412e16 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -43,15 +43,14 @@ #include "web_contents_adapter.h" -#include "browser_accessibility_qt.h" -#include "profile_adapter_client.h" -#include "profile_adapter.h" #include "devtools_frontend_qt.h" #include "download_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/print_view_manager_qt.h" #endif +#include "profile_adapter_client.h" +#include "profile_adapter.h" #include "profile_qt.h" #include "qwebenginecallback_p.h" #include "render_view_observer_host_qt.h" @@ -63,9 +62,11 @@ #include "base/command_line.h" #include "base/run_loop.h" +#include "base/task/post_task.h" #include "base/values.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/download_manager.h" @@ -83,7 +84,9 @@ #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" #include "content/public/common/webrtc_ip_handling_policy.h" -#include "third_party/blink/public/web/web_find_options.h" +#include "extensions/buildflags/buildflags.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" +//#include "third_party/blink/public/web/web_find_options.h" #include "third_party/blink/public/web/web_media_player_action.h" #include "printing/buildflags/buildflags.h" #include "ui/base/clipboard/clipboard.h" @@ -95,6 +98,10 @@ #include <QtWebChannel/QWebChannel> #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/extension_web_contents_observer_qt.h" +#endif + #include <QDir> #include <QGuiApplication> #include <QPageLayout> @@ -105,10 +112,14 @@ #include <QtCore/qelapsedtimer.h> #include <QtCore/qmimedata.h> #include <QtCore/qtemporarydir.h> -#include <QtGui/qaccessible.h> #include <QtGui/qdrag.h> #include <QtGui/qpixmap.h> +// Can't include headers as qaccessible.h conflicts with Chromium headers. +namespace content { +extern QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *acc); +} + namespace QtWebEngineCore { #define CHECK_INITIALIZED(return_value) \ @@ -117,7 +128,7 @@ namespace QtWebEngineCore { #define CHECK_VALID_RENDER_WIDGET_HOST_VIEW(render_view_host) \ if (!render_view_host->IsRenderViewLive() && render_view_host->GetWidget()->GetView()) { \ - qWarning("Ignore navigation due to terminated render process with invalid RenderWidgetHostView."); \ + LOG(WARNING) << "Ignore navigation due to terminated render process with invalid RenderWidgetHostView."; \ return; \ } @@ -190,7 +201,7 @@ static QVariant fromJSValue(const base::Value *result) } case base::Value::Type::BINARY: { - QByteArray data(result->GetBlob().data(), result->GetBlob().size()); + QByteArray data(reinterpret_cast<const char *>(result->GetBlob().data()), result->GetBlob().size()); ret.setValue(data); break; } @@ -210,10 +221,10 @@ static void callbackOnEvaluateJS(WebContentsAdapterClient *adapterClient, quint6 #if QT_CONFIG(webengine_printing_and_pdf) static void callbackOnPrintingFinished(WebContentsAdapterClient *adapterClient, int requestId, - const std::vector<char>& result) + QSharedPointer<QByteArray> result) { if (requestId) - adapterClient->didPrintPage(requestId, QByteArray(result.data(), result.size())); + adapterClient->didPrintPage(requestId, result); } static void callbackOnPdfSavingFinished(WebContentsAdapterClient *adapterClient, @@ -229,9 +240,13 @@ static std::unique_ptr<content::WebContents> createBlankWebContents(WebContentsA content::WebContents::CreateParams create_params(browserContext, NULL); create_params.routing_id = MSG_ROUTING_NONE; create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight); - create_params.context = reinterpret_cast<gfx::NativeView>(adapterClient); create_params.initially_hidden = true; - return content::WebContents::Create(create_params); + + std::unique_ptr<content::WebContents> webContents = content::WebContents::Create(create_params); + WebContentsViewQt* contentsView = static_cast<WebContentsViewQt*>(static_cast<content::WebContentsImpl*>(webContents.get())->GetView()); + contentsView->setClient(adapterClient); + + return webContents; } static void serializeNavigationHistory(const content::NavigationController &controller, QDataStream &output) @@ -457,7 +472,6 @@ void WebContentsAdapter::initialize(content::SiteInstance *site) if (!m_webContents) { content::WebContents::CreateParams create_params(m_profileAdapter->profile(), site); create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight); - create_params.context = reinterpret_cast<gfx::NativeView>(m_adapterClient); create_params.initially_hidden = true; m_webContents = content::WebContents::Create(create_params); } @@ -495,7 +509,7 @@ void WebContentsAdapter::initialize(content::SiteInstance *site) // Let the WebContent's view know about the WebContentsAdapterClient. WebContentsViewQt* contentsView = static_cast<WebContentsViewQt*>(static_cast<content::WebContentsImpl*>(m_webContents.get())->GetView()); - contentsView->initialize(m_adapterClient); + contentsView->setClient(m_adapterClient); // This should only be necessary after having restored the history to a new WebContentsAdapter. m_webContents->GetController().LoadIfNecessary(); @@ -503,6 +517,9 @@ void WebContentsAdapter::initialize(content::SiteInstance *site) #if QT_CONFIG(webengine_printing_and_pdf) PrintViewManagerQt::CreateForWebContents(webContents()); #endif +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::ExtensionWebContentsObserverQt::CreateForWebContents(webContents()); +#endif // Create an instance of WebEngineVisitedLinksManager to catch the first // content::NOTIFICATION_RENDERER_PROCESS_CREATED event. This event will @@ -630,13 +647,12 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) "HTTP-POST data can only be sent over HTTP(S) protocol")); return; } + params.post_data = network::ResourceRequestBody::CreateFromBytes( + (const char*)request.postData().constData(), + request.postData().length()); break; } - params.post_data = network::ResourceRequestBody::CreateFromBytes( - (const char*)request.postData().constData(), - request.postData().length()); - // convert the custom headers into the format that chromium expects QVector<QByteArray> headers = request.headers(); for (QVector<QByteArray>::const_iterator it = headers.cbegin(); it != headers.cend(); ++it) { @@ -662,8 +678,8 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) if (resizeNeeded) { // Schedule navigation on the event loop. - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, base::BindOnce(navigate, this, std::move(params))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(navigate, this, std::move(params))); } else { navigate(this, params); } @@ -800,7 +816,7 @@ void WebContentsAdapter::selectAll() void WebContentsAdapter::requestClose() { CHECK_INITIALIZED(); - m_webContents->DispatchBeforeUnload(); + m_webContents->DispatchBeforeUnload(false /* auto_cancel */); } void WebContentsAdapter::unselect() @@ -932,8 +948,8 @@ QAccessibleInterface *WebContentsAdapter::browserAccessible() if (!manager) // FIXME! return nullptr; content::BrowserAccessibility *acc = manager->GetRoot(); - content::BrowserAccessibilityQt *accQt = static_cast<content::BrowserAccessibilityQt*>(acc); - return accQt; + + return content::toQAccessibleInterface(acc); } #endif // QT_NO_ACCESSIBILITY @@ -990,16 +1006,16 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv m_adapterClient->didFindText(m_lastFindRequestId, 0); } - blink::WebFindOptions options; - options.forward = !findBackward; - options.match_case = caseSensitively; - options.find_next = subString == m_webContentsDelegate->lastSearchedString(); + blink::mojom::FindOptionsPtr options = blink::mojom::FindOptions::New(); + options->forward = !findBackward; + options->match_case = caseSensitively; + options->find_next = subString == m_webContentsDelegate->lastSearchedString(); m_webContentsDelegate->setLastSearchedString(subString); // Find already allows a request ID as input, but only as an int. // Use the same counter but mod it to MAX_INT, this keeps the same likeliness of request ID clashing. int shrunkRequestId = m_nextRequestId++ & 0x7fffffff; - m_webContents->Find(shrunkRequestId, toString16(subString), options); + m_webContents->Find(shrunkRequestId, toString16(subString), std::move(options)); m_lastFindRequestId = shrunkRequestId; return shrunkRequestId; } @@ -1257,6 +1273,12 @@ void WebContentsAdapter::runGeolocationRequestCallback(const QUrl &securityOrigi m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::GeolocationPermission, allowed); } +void WebContentsAdapter::runUserNotificationRequestCallback(const QUrl &securityOrigin, bool allowed) +{ + CHECK_INITIALIZED(); + m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::NotificationPermission, allowed); +} + void WebContentsAdapter::grantMouseLockPermission(bool granted) { CHECK_INITIALIZED(); @@ -1271,16 +1293,6 @@ void WebContentsAdapter::grantMouseLockPermission(bool granted) m_webContents->GotResponseToLockMouseRequest(granted); } -void WebContentsAdapter::dpiScaleChanged() -{ - CHECK_INITIALIZED(); - content::RenderWidgetHostImpl* impl = NULL; - if (m_webContents->GetRenderViewHost()) - impl = content::RenderWidgetHostImpl::From(m_webContents->GetRenderViewHost()->GetWidget()); - if (impl) - impl->NotifyScreenInfoChanged(); -} - void WebContentsAdapter::setBackgroundColor(const QColor &color) { CHECK_INITIALIZED(); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index e8e5359be..c6e4f2256 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -183,8 +183,8 @@ public: void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags); void runGeolocationRequestCallback(const QUrl &securityOrigin, bool allowed); void grantMouseLockPermission(bool granted); + void runUserNotificationRequestCallback(const QUrl &securityOrigin, bool allowed); - void dpiScaleChanged(); void setBackgroundColor(const QColor &color); QAccessibleInterface *browserAccessible(); ProfileQt* profile(); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 28be33c23..cdfcae450 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -66,6 +66,7 @@ QT_FORWARD_DECLARE_CLASS(QKeyEvent) QT_FORWARD_DECLARE_CLASS(QVariant) QT_FORWARD_DECLARE_CLASS(QWebEngineQuotaRequest) QT_FORWARD_DECLARE_CLASS(QWebEngineRegisterProtocolHandlerRequest) +QT_FORWARD_DECLARE_CLASS(QWebEngineUrlRequestInfo) namespace content { struct DropData; @@ -81,6 +82,8 @@ class JavaScriptDialogController; class RenderWidgetHostViewQt; class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateClient; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; class WebContentsDelegateQt; class WebEngineSettings; @@ -421,7 +424,6 @@ public: virtual void selectionChanged() = 0; virtual void recentlyAudibleChanged(bool recentlyAudible) = 0; virtual QRectF viewportRect() const = 0; - virtual qreal dpiScale() const = 0; virtual QColor backgroundColor() const = 0; virtual void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) = 0; virtual void loadCommitted() = 0; @@ -444,7 +446,7 @@ public: virtual void didFetchDocumentMarkup(quint64 requestId, const QString& result) = 0; virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0; virtual void didFindText(quint64 requestId, int matchCount) = 0; - virtual void didPrintPage(quint64 requestId, const QByteArray &result) = 0; + virtual void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) = 0; virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0; virtual void passOnFocus(bool reverse) = 0; // returns the last QObject (QWidget/QQuickItem) based object in the accessibility @@ -457,6 +459,7 @@ public: virtual void runMouseLockPermissionRequest(const QUrl &securityOrigin) = 0; virtual void runQuotaRequest(QWebEngineQuotaRequest) = 0; virtual void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) = 0; + virtual void runUserNotificationPermissionRequest(const QUrl &securityOrigin) = 0; virtual WebEngineSettings *webEngineSettings() const = 0; RenderProcessTerminationStatus renderProcessExitStatus(int); virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) = 0; @@ -475,6 +478,10 @@ public: virtual ClientType clientType() = 0; virtual void printRequested() = 0; virtual void widgetChanged(RenderWidgetHostViewQtDelegate *newWidget) = 0; + virtual void interceptRequest(QWebEngineUrlRequestInfo &) { } + virtual TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) = 0; + virtual void showTouchSelectionMenu(TouchSelectionMenuController *menuController, const QRect &bounds, const QSize &handleSize) = 0; + virtual void hideTouchSelectionMenu() = 0; virtual ProfileAdapter *profileAdapter() = 0; virtual WebContentsAdapter* webContentsAdapter() = 0; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 4bde93fd3..ade29c770 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -58,13 +58,16 @@ #include "visited_links_manager_qt.h" #include "web_contents_adapter_client.h" #include "web_contents_adapter.h" +#include "web_contents_view_qt.h" #include "web_engine_context.h" #include "web_engine_settings.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/file_select_listener.h" #include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" @@ -73,7 +76,6 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/favicon_url.h" -#include "content/public/common/file_chooser_params.h" #include "content/public/common/frame_navigate_params.h" #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" @@ -414,9 +416,11 @@ void WebContentsDelegateQt::DidUpdateFaviconURL(const std::vector<content::Favic void WebContentsDelegateQt::WebContentsCreated(content::WebContents */*source_contents*/, int /*opener_render_process_id*/, int /*opener_render_frame_id*/, const std::string &/*frame_name*/, - const GURL &target_url, content::WebContents */*new_contents*/) + const GURL &target_url, content::WebContents *newContents) { m_initialTargetUrl = toQt(target_url); + if (auto *view = static_cast<content::WebContentsImpl *>(newContents)->GetView()) + static_cast<WebContentsViewQt *>(view)->setFactoryClient(m_viewClient); } content::ColorChooser *WebContentsDelegateQt::OpenColorChooser(content::WebContents *source, SkColor color, const std::vector<blink::mojom::ColorSuggestionPtr> &suggestion) @@ -451,12 +455,14 @@ bool WebContentsDelegateQt::IsFullscreenForTabOrPending(const content::WebConten return m_viewClient->isFullScreenMode(); } -ASSERT_ENUMS_MATCH(FilePickerController::Open, content::FileChooserParams::Open) -ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, content::FileChooserParams::OpenMultiple) -ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, content::FileChooserParams::UploadFolder) -ASSERT_ENUMS_MATCH(FilePickerController::Save, content::FileChooserParams::Save) +ASSERT_ENUMS_MATCH(FilePickerController::Open, blink::mojom::FileChooserParams::Mode::kOpen) +ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, blink::mojom::FileChooserParams::Mode::kOpenMultiple) +ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, blink::mojom::FileChooserParams::Mode::kUploadFolder) +ASSERT_ENUMS_MATCH(FilePickerController::Save, blink::mojom::FileChooserParams::Mode::kSave) -void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost *frameHost, const content::FileChooserParams ¶ms) +void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost * /*frameHost*/, + std::unique_ptr<content::FileSelectListener> listener, + const blink::mojom::FileChooserParams& params) { QStringList acceptedMimeTypes; acceptedMimeTypes.reserve(params.accept_types.size()); @@ -464,7 +470,7 @@ void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost *frameHost, acceptedMimeTypes.append(toQt(*it)); m_filePickerController.reset(new FilePickerController(static_cast<FilePickerController::FileChooserMode>(params.mode), - frameHost, toQt(params.default_file_name.value()), acceptedMimeTypes)); + std::move(listener), toQt(params.default_file_name.value()), acceptedMimeTypes)); // Defer the call to not block base::MessageLoop::RunTask with modal dialogs. QTimer::singleShot(0, [this] () { @@ -583,6 +589,11 @@ void WebContentsDelegateQt::requestGeolocationPermission(const QUrl &requestingO m_viewClient->runGeolocationPermissionRequest(requestingOrigin); } +void WebContentsDelegateQt::requestUserNotificationPermission(const QUrl &requestingOrigin) +{ + m_viewClient->runUserNotificationPermissionRequest(requestingOrigin); +} + extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition); void WebContentsDelegateQt::launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture) @@ -633,7 +644,9 @@ void WebContentsDelegateQt::BeforeUnloadFired(content::WebContents *tab, bool pr m_viewClient->windowCloseRejected(); } -void WebContentsDelegateQt::BeforeUnloadFired(const base::TimeTicks &proceed_time) { +void WebContentsDelegateQt::BeforeUnloadFired(bool proceed, const base::TimeTicks &proceed_time) +{ + Q_UNUSED(proceed); Q_UNUSED(proceed_time); } diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 9c0f8f484..77f6cc389 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -114,7 +114,9 @@ public: void EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL &origin, const blink::WebFullscreenOptions &) override; void ExitFullscreenModeForTab(content::WebContents*) override; bool IsFullscreenForTabOrPending(const content::WebContents* web_contents) const override; - void RunFileChooser(content::RenderFrameHost* render_frame_host, const content::FileChooserParams& params) override; + void RunFileChooser(content::RenderFrameHost* render_frame_host, + std::unique_ptr<content::FileSelectListener> listener, + const blink::mojom::FileChooserParams& params) override; bool DidAddMessageToConsole(content::WebContents* source, int32_t level, const base::string16& message, int32_t line_no, const base::string16& source_id) override; void FindReply(content::WebContents *source, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) override; void RequestMediaAccessPermission(content::WebContents *web_contents, @@ -135,7 +137,7 @@ public: void DidFinishNavigation(content::NavigationHandle *navigation_handle) override; void DidFailLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) override; void DidFinishLoad(content::RenderFrameHost *render_frame_host, const GURL &validated_url) override; - void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; + void BeforeUnloadFired(bool proceed, const base::TimeTicks& proceed_time) override; void DidUpdateFaviconURL(const std::vector<content::FaviconURL> &candidates) override; void OnVisibilityChanged(content::Visibility visibility) override; void DidFirstVisuallyNonEmptyPaint() override; @@ -146,6 +148,7 @@ public: void allowCertificateError(const QSharedPointer<CertificateErrorController> &); void selectClientCert(const QSharedPointer<ClientCertSelectController> &); void requestGeolocationPermission(const QUrl &requestingOrigin); + void requestUserNotificationPermission(const QUrl &requestingOrigin); void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture); FaviconManager *faviconManager(); diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp index 3c4465ae3..7177a8713 100644 --- a/src/core/web_contents_view_qt.cpp +++ b/src/core/web_contents_view_qt.cpp @@ -41,24 +41,40 @@ #include "profile_adapter.h" #include "content_browser_client_qt.h" +#include "render_widget_host_view_qt.h" #include "render_widget_host_view_qt_delegate.h" #include "render_widget_host_view_qt.h" +#include "touch_selection_controller_client_qt.h" #include "type_conversion.h" +#include "web_contents_adapter_client.h" #include "web_contents_adapter.h" #include "web_engine_context.h" +#include "web_contents_delegate_qt.h" #include "components/spellcheck/spellcheck_buildflags.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/common/context_menu_params.h" -#include <ui/gfx/image/image_skia.h> +#include "ui/gfx/image/image_skia.h" #include <QtGui/qpixmap.h> namespace QtWebEngineCore { -void WebContentsViewQt::initialize(WebContentsAdapterClient* client) +void WebContentsViewQt::setFactoryClient(WebContentsAdapterClient* client) +{ + if (m_factoryClient) + return; + m_factoryClient = client; + + // Check if a RWHV was created before the pre-initialization. + if (auto view = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) { + view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view)); + } +} + +void WebContentsViewQt::setClient(WebContentsAdapterClient* client) { m_client = client; m_factoryClient = client; @@ -74,15 +90,16 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForWidget(conten { RenderWidgetHostViewQt *view = new RenderWidgetHostViewQt(render_widget_host); - Q_ASSERT(m_factoryClient); - view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view)); - if (m_client) - view->setAdapterClient(m_client); + if (m_factoryClient) { + view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view)); + if (m_client) + view->setAdapterClient(m_client); + } return view; } -content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForPopupWidget(content::RenderWidgetHost* render_widget_host) +content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForChildWidget(content::RenderWidgetHost* render_widget_host) { RenderWidgetHostViewQt *view = new RenderWidgetHostViewQt(render_widget_host); @@ -95,15 +112,11 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForPopupWidget(c void WebContentsViewQt::CreateView(const gfx::Size& initial_size, gfx::NativeView context) { - // This is passed through content::WebContents::CreateParams::context either as the native view's client - // directly or, in the case of a page-created new window, the client of the creating window's native view. - m_factoryClient = reinterpret_cast<WebContentsAdapterClient *>(context); } gfx::NativeView WebContentsViewQt::GetNativeView() const { - // Hack to provide the client to WebContentsImpl::CreateNewWindow. - return reinterpret_cast<gfx::NativeView>(m_client); + return nullptr; } void WebContentsViewQt::GetContainerBounds(gfx::Rect* out) const @@ -186,6 +199,11 @@ static inline WebEngineContextMenuData fromParams(const content::ContextMenuPara void WebContentsViewQt::ShowContextMenu(content::RenderFrameHost *, const content::ContextMenuParams ¶ms) { + if (auto rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) { + if (rwhv && rwhv->getTouchSelectionControllerClient()->handleContextMenu(params)) + return; + } + WebEngineContextMenuData contextMenuData(fromParams(params)); #if QT_CONFIG(webengine_spellchecker) // Do not use params.spellcheck_enabled, since it is never @@ -230,7 +248,7 @@ void WebContentsViewQt::StartDragging(const content::DropData &drop_data, QPixmap pixmap; QPoint hotspot; - pixmap = QPixmap::fromImage(toQImage(image.GetRepresentation(m_client->dpiScale()))); + pixmap = QPixmap::fromImage(toQImage(image.GetRepresentation(1.0))); if (!pixmap.isNull()) { hotspot.setX(image_offset.x()); hotspot.setY(image_offset.y()); diff --git a/src/core/web_contents_view_qt.h b/src/core/web_contents_view_qt.h index d1a2ff81e..7cd3910f2 100644 --- a/src/core/web_contents_view_qt.h +++ b/src/core/web_contents_view_qt.h @@ -63,17 +63,17 @@ public: : m_webContents(webContents) , m_client(nullptr) , m_factoryClient(nullptr) - , m_allowOtherViews(false) { } - void initialize(WebContentsAdapterClient* client); + void setFactoryClient(WebContentsAdapterClient* client); + void setClient(WebContentsAdapterClient* client); WebContentsAdapterClient *client() { return m_client; } content::RenderWidgetHostViewBase *CreateViewForWidget(content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) override; void CreateView(const gfx::Size& initial_size, gfx::NativeView context) override; - content::RenderWidgetHostViewBase* CreateViewForPopupWidget(content::RenderWidgetHost* render_widget_host) override; + content::RenderWidgetHostViewBase *CreateViewForChildWidget(content::RenderWidgetHost* render_widget_host) override; void SetPageTitle(const base::string16& title) override { } @@ -83,29 +83,29 @@ public: void RenderViewHostChanged(content::RenderViewHost*, content::RenderViewHost*) override { } - void SetOverscrollControllerEnabled(bool enabled) override { QT_NOT_YET_IMPLEMENTED } + void SetOverscrollControllerEnabled(bool enabled) override { } gfx::NativeView GetNativeView() const override; - gfx::NativeView GetContentNativeView() const override { QT_NOT_USED return 0; } + gfx::NativeView GetContentNativeView() const override { return nullptr; } - gfx::NativeWindow GetTopLevelNativeWindow() const override { QT_NOT_USED return 0; } + gfx::NativeWindow GetTopLevelNativeWindow() const override { return nullptr; } void GetContainerBounds(gfx::Rect* out) const override; - void SizeContents(const gfx::Size& size) override { QT_NOT_YET_IMPLEMENTED } + void SizeContents(const gfx::Size& size) override { } void Focus() override; void SetInitialFocus() override; - void StoreFocus() override { QT_NOT_USED } + void StoreFocus() override { } - void RestoreFocus() override { QT_NOT_USED } + void RestoreFocus() override { } content::DropData* GetDropData() const override { QT_NOT_YET_IMPLEMENTED return nullptr; } - gfx::Rect GetViewBounds() const override { QT_NOT_YET_IMPLEMENTED return gfx::Rect(); } + gfx::Rect GetViewBounds() const override { return gfx::Rect(); } void StartDragging(const content::DropData& drop_data, blink::WebDragOperationsMask allowed_ops, const gfx::ImageSkia& image, const gfx::Vector2d& image_offset, @@ -122,8 +122,6 @@ public: #if defined(OS_MACOSX) - void SetAllowOtherViews(bool allow) override { m_allowOtherViews = allow; } - bool GetAllowOtherViews() const override { return m_allowOtherViews; } void CloseTabAfterEventTracking() override { QT_NOT_YET_IMPLEMENTED } bool IsEventTracking() const override { QT_NOT_YET_IMPLEMENTED; return false; } #endif // defined(OS_MACOSX) @@ -132,7 +130,6 @@ private: content::WebContents *m_webContents; WebContentsAdapterClient *m_client; WebContentsAdapterClient *m_factoryClient; - bool m_allowOtherViews; }; } // namespace QtWebEngineCore diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index edae5c47d..e9a941bf2 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -46,21 +46,23 @@ #include "base/files/file_path.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "cc/base/switches.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "chrome/browser/printing/print_job_manager.h" +#include "components/printing/browser/features.h" #endif #include "components/viz/common/features.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/browser/devtools/devtools_http_handler.h" -#include "content/browser/gpu/gpu_main_thread_factory.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/utility_process_host.h" -#include "content/gpu/in_process_gpu_thread.h" +#include "content/browser/scheduler/browser_task_executor.h" +#include "content/browser/startup_helper.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_runner.h" #include "content/public/browser/browser_main_runner.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/plugin_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" @@ -68,9 +70,8 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" -#include "content/renderer/in_process_renderer_thread.h" -#include "content/utility/in_process_utility_thread.h" #include "gpu/command_buffer/service/gpu_switches.h" +#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/ipc/host/gpu_switches.h" #include "media/audio/audio_manager.h" #include "mojo/core/embedder/embedder.h" @@ -86,20 +87,24 @@ #include "content/public/app/sandbox_helper_win.h" #endif // OS_WIN +#ifndef QT_NO_ACCESSIBILITY +#include "accessibility_activation_observer.h" +#endif #include "api/qwebengineurlscheme.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "content_client_qt.h" #include "content_main_delegate_qt.h" #include "devtools_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/webui_controller_factory_qt.h" -#include "type_conversion.h" #include "ozone/gl_context_qt.h" +#include "profile_adapter.h" +#include "type_conversion.h" #include "web_engine_library_info.h" #include <QFileInfo> #include <QGuiApplication> +#include <QMutex> #include <QOffscreenSurface> #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -108,7 +113,9 @@ #include <QStringList> #include <QSurfaceFormat> #include <QVector> -#include <qpa/qplatformnativeinterface.h> +#include <QNetworkProxy> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/private/qguiapplication_p.h> using namespace QtWebEngineCore; @@ -132,10 +139,13 @@ bool usingANGLE() #endif } -bool usingQtQuick2DRenderer() +bool usingDefaultSGBackend() { const QStringList args = QGuiApplication::arguments(); - QString device; + + //folow logic from contextFactory in src/quick/scenegraph/qsgcontextplugin.cpp + QString device = QQuickWindow::sceneGraphBackend(); + for (int index = 0; index < args.count(); ++index) { if (args.at(index).startsWith(QLatin1String("--device="))) { device = args.at(index).mid(9); @@ -144,16 +154,11 @@ bool usingQtQuick2DRenderer() } if (device.isEmpty()) - device = QQuickWindow::sceneGraphBackend(); - if (device.isEmpty()) device = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND")); if (device.isEmpty()) device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE")); - if (device.isEmpty()) - device = QLatin1String("default"); - // Anything other than the default OpenGL device will need to render in 2D mode. - return device != QLatin1String("default"); + return device.isEmpty(); } #endif //QT_NO_OPENGL #if QT_CONFIG(webengine_pepper_plugins) @@ -166,6 +171,8 @@ void dummyGetPluginCallback(const std::vector<content::WebPluginInfo>&) namespace QtWebEngineCore { +extern std::unique_ptr<base::MessagePump> messagePumpFactory(); + bool usingSoftwareDynamicGL() { if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL)) @@ -224,6 +231,12 @@ void WebEngineContext::destroy() { if (m_devtoolsServer) m_devtoolsServer->stop(); + + // Normally the GPU thread is shut down when the GpuProcessHost is destroyed + // on IO thread (triggered by ~BrowserMainRunner). But by that time the UI + // task runner is not working anymore so we need to do this earlier. + destroyGpuProcess(); + base::MessagePump::Delegate *delegate = static_cast<base::MessageLoop *>(m_runLoop->delegate_); // Flush the UI message loop before quitting. @@ -273,6 +286,7 @@ WebEngineContext::~WebEngineContext() Q_ASSERT(!m_devtoolsServer); Q_ASSERT(!m_browserRunner); Q_ASSERT(m_profileAdapters.isEmpty()); + delete s_syncPointManager.fetchAndStoreRelaxed(nullptr); } WebEngineContext *WebEngineContext::current() @@ -312,7 +326,7 @@ void WebEngineContext::destroyContextPostRoutine() // Destroy WebEngineContext before its static pointer is zeroed and destructor called. // Before destroying MessageLoop via destroying BrowserMainRunner destructor // WebEngineContext's pointer is used. - m_handle->destroy(); + m_handle->destroy(); #if !defined(NDEBUG) if (!m_handle->HasOneRef()) qWarning("WebEngineContext leaked on exit, likely due to leaked WebEngine View or Page"); @@ -321,6 +335,18 @@ void WebEngineContext::destroyContextPostRoutine() m_destroyed = true; } +ProxyAuthentication WebEngineContext::qProxyNetworkAuthentication(QString host, int port) +{ + if (!QNetworkProxyFactory::usesSystemConfiguration()) { + QNetworkProxy proxy = QNetworkProxy::applicationProxy(); + if (host == proxy.hostName() && port == proxy.port() && !proxy.user().isEmpty() + && !proxy.password().isEmpty()) { + return std::make_tuple(true, proxy.user(), proxy.password()); + } + } + return std::make_tuple(false, QString(), QString()); +} + #ifndef CHROMIUM_VERSION #error Chromium version should be defined at gyp-time. Something must have gone wrong #define CHROMIUM_VERSION // This is solely to keep Qt Creator happy. @@ -347,6 +373,7 @@ WebEngineContext::WebEngineContext() base::TaskScheduler::Create("Browser"); m_contentRunner.reset(content::ContentMainRunner::Create()); m_browserRunner.reset(content::BrowserMainRunner::Create()); + #ifdef Q_OS_LINUX // Call qputenv before BrowserMainRunnerImpl::Initialize is called. // http://crbug.com/245466 @@ -367,33 +394,22 @@ WebEngineContext::WebEngineContext() // Allow us to inject javascript like any webview toolkit. content::RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView(); - base::CommandLine::CreateEmpty(); - base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); QStringList appArgs = QCoreApplication::arguments(); - if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { - appArgs = appArgs.mid(0, 1); // Take application name and drop the rest - appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); - } - bool enableWebGLSoftwareRendering = - appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); + // If user requested GL support instead of using Skia rendering to + // bitmaps, use software rendering via software OpenGL. This might be less + // performant, but at least provides WebGL support. + // TODO(miklocek), check if this still works with latest chromium + bool enableGLSoftwareRendering = appArgs.contains(QStringLiteral("--enable-webgl-software-rendering")); bool useEmbeddedSwitches = false; #if defined(QTWEBENGINE_EMBEDDED_SWITCHES) - useEmbeddedSwitches = !appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); + useEmbeddedSwitches = !appArgs.contains(QStringLiteral("--disable-embedded-switches")); #else - useEmbeddedSwitches = appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); + useEmbeddedSwitches = appArgs.contains(QStringLiteral("--enable-embedded-switches")); #endif - base::CommandLine::StringVector argv; - argv.resize(appArgs.size()); -#if defined(Q_OS_WIN) - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = toString16(appArgs[i]); -#else - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = appArgs[i].toStdString(); -#endif - parsedCommandLine->InitFromArgv(argv); + + base::CommandLine* parsedCommandLine = commandLine(); parsedCommandLine->AppendSwitchPath(switches::kBrowserSubprocessPath, WebEngineLibraryInfo::getPath(content::CHILD_PROCESS_EXE)); @@ -426,8 +442,6 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisablePepper3DImageChromium); // Same problem with select popups. parsedCommandLine->AppendSwitch(switches::kDisableNativeGpuMemoryBuffers); - // SandboxV2 doesn't currently work for us - appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kMacV2Sandbox.name); #endif #if defined(Q_OS_WIN) @@ -435,10 +449,23 @@ WebEngineContext::WebEngineContext() // an OpenGL Core Profile context. If the switch is not set, it would always try to create a // Core Profile context, even if Qt uses a legacy profile, which causes // "Could not share GL contexts" warnings, because it's not possible to share between Core and - // legacy profiles. - // Given that Core profile is not currently supported on Windows anyway, pass this switch to - // get rid of the warnings. - parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); + // legacy profiles. See GLContextWGL::Initialize(). + // Given that Desktop GL Core profile is not currently supported on Windows anyway, pass this + // switch to get rid of the warnings. + // + // The switch is also used to determine which version of OpenGL ES to use (2 or 3) when using + // ANGLE. + // If the switch is not set, Chromium will always try to create an ES3 context, even if Qt uses + // an ES2 context, which causes resource sharing issues (black screen), + // see gpu::gles2::GenerateGLContextAttribs(). + // Make sure to disable ES3 context creation when using ES2. + const bool isGLES2Context = qt_gl_global_share_context() + && qt_gl_global_share_context()->isOpenGLES() + && qt_gl_global_share_context()->format().majorVersion() == 2; + const bool isDesktopGLOrSoftware = !usingANGLE(); + + if (isDesktopGLOrSoftware || isGLES2Context) + parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); #endif // Needed to allow navigations within pages that were set using setHtml(). One example is // tst_QWebEnginePage::acceptNavigationRequest. @@ -449,8 +476,12 @@ WebEngineContext::WebEngineContext() appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kEnableSurfaceSynchronization.name); // The video-capture service is not functioning at this moment (since 69) appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kMojoVideoCapture.name); - // We do not yet support the internal video capture API. - appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kUseVideoCaptureApiForDevToolsSnapshots.name); + + appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kBackgroundFetch.name); + +#if QT_CONFIG(webengine_printing_and_pdf) + appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, printing::features::kUsePdfCompositorServiceForPrint.name); +#endif if (useEmbeddedSwitches) { // embedded switches are based on the switches for Android, see content/browser/android/content_startup_flags.cc @@ -470,22 +501,18 @@ WebEngineContext::WebEngineContext() const char *glType = 0; #ifndef QT_NO_OPENGL - bool tryGL = - !usingANGLE() - && (!usingSoftwareDynamicGL() - // If user requested WebGL support instead of using Skia rendering to - // bitmaps, use software rendering via software OpenGL. This might be less - // performant, but at least provides WebGL support. - || enableWebGLSoftwareRendering - ) - && !usingQtQuick2DRenderer(); - + const bool tryGL = (usingDefaultSGBackend() && !usingSoftwareDynamicGL() && + QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) + || enableGLSoftwareRendering; if (tryGL) { if (qt_gl_global_share_context() && qt_gl_global_share_context()->isValid()) { - // If the native handle is QEGLNativeContext try to use GL ES/2, if there is no native handle - // assume we are using wayland and try GL ES/2, and finally Ozone demands GL ES/2 too. + // If the native handle is QEGLNativeContext try to use GL ES/2. + // If there is no native handle, assume we are using wayland and try GL ES/2. + // If we are using ANGLE on Windows, use OpenGL ES (2 or 3). if (qt_gl_global_share_context()->nativeHandle().isNull() - || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), "QEGLNativeContext")) + || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), + "QEGLNativeContext") + || usingANGLE()) { if (qt_gl_global_share_context()->isOpenGLES()) { glType = gl::kGLImplementationEGLName; @@ -543,7 +570,7 @@ WebEngineContext::WebEngineContext() if (glType) { parsedCommandLine->AppendSwitchASCII(switches::kUseGL, glType); parsedCommandLine->AppendSwitch(switches::kInProcessGPU); - if (enableWebGLSoftwareRendering) { + if (enableGLSoftwareRendering) { parsedCommandLine->AppendSwitch(switches::kDisableGpuRasterization); parsedCommandLine->AppendSwitch(switches::kIgnoreGpuBlacklist); } @@ -551,9 +578,16 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisableGpu); } - content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); - content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); - content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + bool threadedGpu = true; +#ifndef QT_NO_OPENGL + threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); +#endif + registerMainThreadFactories(threadedGpu); + + SetContentClient(new ContentClientQt); + + content::StartBrowserTaskScheduler(); + content::BrowserTaskExecutor::Create(); mojo::core::Init(); @@ -605,6 +639,10 @@ WebEngineContext::WebEngineContext() m_printJobManager.reset(new printing::PrintJobManager()); #endif +#ifndef QT_NO_ACCESSIBILITY + m_accessibilityActivationObserver.reset(new AccessibilityActivationObserver()); +#endif + content::WebUIControllerFactory::RegisterFactory(WebUIControllerFactoryQt::GetInstance()); } @@ -614,4 +652,48 @@ printing::PrintJobManager* WebEngineContext::getPrintJobManager() return m_printJobManager.get(); } #endif + +static QMutex s_spmMutex; +QAtomicPointer<gpu::SyncPointManager> WebEngineContext::s_syncPointManager; + +gpu::SyncPointManager *WebEngineContext::syncPointManager() +{ + if (gpu::SyncPointManager *spm = s_syncPointManager.loadAcquire()) + return spm; + QMutexLocker lock(&s_spmMutex); + if (!s_syncPointManager) + s_syncPointManager.store(new gpu::SyncPointManager()); + return s_syncPointManager.load(); +} + +base::CommandLine* WebEngineContext::commandLine() { + if (base::CommandLine::CreateEmpty()) { + base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); + QStringList appArgs = QCoreApplication::arguments(); + if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { + appArgs = appArgs.mid(0, 1); // Take application name and drop the rest + appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); + } +#ifdef Q_OS_WIN + appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); +#endif + appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); + appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); + + base::CommandLine::StringVector argv; + argv.resize(appArgs.size()); +#if defined(Q_OS_WIN) + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = toString16(appArgs[i]); +#else + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = appArgs[i].toStdString(); +#endif + parsedCommandLine->InitFromArgv(argv); + return parsedCommandLine; + } else { + return base::CommandLine::ForCurrentProcess(); + } +} + } // namespace diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h index 604c85a61..2364bacbe 100644 --- a/src/core/web_engine_context.h +++ b/src/core/web_engine_context.h @@ -47,11 +47,20 @@ namespace base { class RunLoop; +class CommandLine; } namespace content { class BrowserMainRunner; class ContentMainRunner; +class GpuProcess; +class GpuThreadController; +class InProcessChildThreadParams; +} + +namespace gpu { +struct GpuPreferences; +class SyncPointManager; } #if QT_CONFIG(webengine_printing_and_pdf) @@ -64,16 +73,20 @@ QT_FORWARD_DECLARE_CLASS(QObject) namespace QtWebEngineCore { -class ProfileAdapter; +class AccessibilityActivationObserver; class ContentMainDelegateQt; class DevToolsServerQt; +class ProfileAdapter; bool usingSoftwareDynamicGL(); +typedef std::tuple<bool, QString, QString> ProxyAuthentication; + class WebEngineContext : public base::RefCounted<WebEngineContext> { public: static WebEngineContext *current(); static void destroyContextPostRoutine(); + static ProxyAuthentication qProxyNetworkAuthentication(QString host, int port); ProfileAdapter *createDefaultProfileAdapter(); ProfileAdapter *defaultProfileAdapter(); @@ -86,6 +99,9 @@ public: void addProfileAdapter(ProfileAdapter *profileAdapter); void removeProfileAdapter(ProfileAdapter *profileAdapter); void destroy(); + static base::CommandLine* commandLine(); + + static gpu::SyncPointManager *syncPointManager(); private: friend class base::RefCounted<WebEngineContext>; @@ -93,6 +109,9 @@ private: WebEngineContext(); ~WebEngineContext(); + static void registerMainThreadFactories(bool threaded); + static void destroyGpuProcess(); + std::unique_ptr<base::RunLoop> m_runLoop; std::unique_ptr<ContentMainDelegateQt> m_mainDelegate; std::unique_ptr<content::ContentMainRunner> m_contentRunner; @@ -101,12 +120,16 @@ private: std::unique_ptr<ProfileAdapter> m_defaultProfileAdapter; std::unique_ptr<DevToolsServerQt> m_devtoolsServer; QVector<ProfileAdapter*> m_profileAdapters; +#ifndef QT_NO_ACCESSIBILITY + std::unique_ptr<AccessibilityActivationObserver> m_accessibilityActivationObserver; +#endif #if QT_CONFIG(webengine_printing_and_pdf) std::unique_ptr<printing::PrintJobManager> m_printJobManager; #endif static scoped_refptr<QtWebEngineCore::WebEngineContext> m_handle; static bool m_destroyed; + static QAtomicPointer<gpu::SyncPointManager> s_syncPointManager; }; } // namespace diff --git a/src/core/web_engine_context_threads.cpp b/src/core/web_engine_context_threads.cpp new file mode 100644 index 000000000..07a86cc69 --- /dev/null +++ b/src/core/web_engine_context_threads.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "web_engine_context.h" + +#include "base/bind.h" +#include "base/task/post_task.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_restrictions.h" +#include "content/browser/gpu/gpu_main_thread_factory.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_process_host.h" +#include "content/gpu/gpu_child_thread.h" +#include "content/gpu/gpu_process.h" +#include "content/gpu/in_process_gpu_thread.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/renderer/in_process_renderer_thread.h" +#include "content/utility/in_process_utility_thread.h" + +#include <memory> + +namespace QtWebEngineCore { + +struct GpuThreadControllerQt : content::GpuThreadController +{ + GpuThreadControllerQt(const content::InProcessChildThreadParams ¶ms, const gpu::GpuPreferences &gpuPreferences) + { + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&GpuThreadControllerQt::createGpuProcess, params, gpuPreferences)); + } + ~GpuThreadControllerQt() override + { + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&GpuThreadControllerQt::destroyGpuProcess)); + } + + static void createGpuProcess( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + s_gpuProcess = std::make_unique<content::GpuProcess>(base::ThreadPriority::NORMAL); + auto gpuInit = std::make_unique<gpu::GpuInit>(); + gpuInit->InitializeInProcess(base::CommandLine::ForCurrentProcess(), gpuPreferences); + auto childThread = new content::GpuChildThread(params, std::move(gpuInit)); + childThread->Init(base::Time::Now()); + s_gpuProcess->set_main_thread(childThread); + } + + static void destroyGpuProcess() + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + // viz::GpuServiceImpl::~GpuServiceImpl waits for io task. + base::ScopedAllowBaseSyncPrimitivesForTesting allow; + s_gpuProcess.reset(); + s_gpuProcessDestroyed = true; + } + + static std::unique_ptr<content::GpuProcess> s_gpuProcess; + static bool s_gpuProcessDestroyed; +}; + +std::unique_ptr<content::GpuProcess> GpuThreadControllerQt::s_gpuProcess; +bool GpuThreadControllerQt::s_gpuProcessDestroyed = false; + +static std::unique_ptr<content::GpuThreadController> createGpuThreadController( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) +{ + return std::make_unique<GpuThreadControllerQt>(params, gpuPreferences); +} + +// static +void WebEngineContext::destroyGpuProcess() +{ + GpuThreadControllerQt::destroyGpuProcess(); +} + +// static +void WebEngineContext::registerMainThreadFactories(bool threaded) +{ + content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); + content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); + if (threaded) + content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + else + content::RegisterGpuMainThreadFactory(createGpuThreadController); +} + +} // namespace QtWebEngineCore diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp index 3899ced25..12ffd91e6 100644 --- a/src/core/web_engine_library_info.cpp +++ b/src/core/web_engine_library_info.cpp @@ -54,6 +54,7 @@ #include <QDir> #include <QFileInfo> #include <QLibraryInfo> +#include <QLocale> #include <QStandardPaths> #include <QString> diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp index 664951e66..705c0c106 100644 --- a/src/core/web_engine_settings.cpp +++ b/src/core/web_engine_settings.cpp @@ -283,6 +283,11 @@ void WebEngineSettings::initDefaults() s_defaultAttributes.insert(WebRTCPublicInterfacesOnly, false); s_defaultAttributes.insert(JavascriptCanPaste, false); s_defaultAttributes.insert(DnsPrefetchEnabled, false); +#if QT_CONFIG(webengine_extensions) + s_defaultAttributes.insert(PDFViewerEnabled, true); +#else + s_defaultAttributes.insert(PDFViewerEnabled, false); +#endif } if (s_defaultFontFamilies.isEmpty()) { diff --git a/src/core/web_engine_settings.h b/src/core/web_engine_settings.h index 8930d7c27..8e1bb741c 100644 --- a/src/core/web_engine_settings.h +++ b/src/core/web_engine_settings.h @@ -103,6 +103,7 @@ public: WebRTCPublicInterfacesOnly, JavascriptCanPaste, DnsPrefetchEnabled, + PDFViewerEnabled, }; // Must match the values from the public API in qwebenginesettings.h. diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index fc6287dd9..b97696030 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -101,7 +101,7 @@ static KeyboardDriver keyboardDriverImpl() if (platformName == QLatin1Literal("xcb") || platformName == QLatin1Literal("wayland")) return KeyboardDriver::Xkb; -#if QT_CONFIG(libinput) && QT_CONFIG(xkbcommon) +#if QT_CONFIG(libinput) // Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey. if (platformName == QLatin1Literal("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT")) return KeyboardDriver::Xkb; @@ -1354,10 +1354,10 @@ static WebPointerProperties::PointerType pointerTypeForTabletEvent(const QTablet } #endif -WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev, double dpiScale) +WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev) { WebMouseEvent webKitEvent(webEventTypeForEvent(ev), - WebFloatPoint(ev->x() / dpiScale, ev->y() / dpiScale), + WebFloatPoint(ev->x(), ev->y()), WebFloatPoint(ev->globalX(), ev->globalY()), mouseButtonForEvent<QMouseEvent>(ev), 0, @@ -1369,14 +1369,14 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev, double dpiScale) return webKitEvent; } -WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale) +WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev) { WebMouseEvent webKitEvent; webKitEvent.SetTimeStamp(base::TimeTicks::Now()); webKitEvent.SetModifiers(modifiersForEvent(ev)); webKitEvent.SetType(webEventTypeForEvent(ev)); - webKitEvent.SetPositionInWidget(ev->pos().x() / dpiScale, ev->pos().y() / dpiScale); + webKitEvent.SetPositionInWidget(ev->pos().x(), ev->pos().y()); webKitEvent.movement_x = ev->pos().x() - ev->oldPos().x(); webKitEvent.movement_y = ev->pos().y() - ev->oldPos().y(); webKitEvent.pointer_type = WebPointerProperties::PointerType::kMouse; @@ -1385,10 +1385,10 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale) } #if QT_CONFIG(tabletevent) -WebMouseEvent WebEventFactory::toWebMouseEvent(QTabletEvent *ev, double dpiScale) +WebMouseEvent WebEventFactory::toWebMouseEvent(QTabletEvent *ev) { WebMouseEvent webKitEvent(webEventTypeForEvent(ev), - WebFloatPoint(ev->x() / dpiScale, ev->y() / dpiScale), + WebFloatPoint(ev->x(), ev->y()), WebFloatPoint(ev->globalX(), ev->globalY()), mouseButtonForEvent<QTabletEvent>(ev), 0, @@ -1416,17 +1416,17 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QEvent *ev) } #ifndef QT_NO_GESTURES -WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev, double dpiScale) +WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev) { WebGestureEvent webKitEvent; webKitEvent.SetTimeStamp(base::TimeTicks::Now()); webKitEvent.SetModifiers(modifiersForEvent(ev)); - webKitEvent.SetPositionInWidget(WebFloatPoint(ev->localPos().x() / dpiScale, - ev->localPos().y() / dpiScale)); + webKitEvent.SetPositionInWidget(WebFloatPoint(ev->localPos().x(), + ev->localPos().y())); - webKitEvent.SetPositionInScreen(WebFloatPoint(ev->screenPos().x() / dpiScale, - ev->screenPos().y() / dpiScale)); + webKitEvent.SetPositionInScreen(WebFloatPoint(ev->screenPos().x(), + ev->screenPos().y())); webKitEvent.SetSourceDevice(blink::kWebGestureDeviceTouchpad); @@ -1484,13 +1484,13 @@ blink::WebMouseWheelEvent::Phase toBlinkPhase(QWheelEvent *ev) return blink::WebMouseWheelEvent::kPhaseNone; } -blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale) +blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev) { WebMouseWheelEvent webEvent; webEvent.SetType(webEventTypeForEvent(ev)); webEvent.SetModifiers(modifiersForEvent(ev)); webEvent.SetTimeStamp(base::TimeTicks::Now()); - webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); + webEvent.SetPositionInWidget(ev->x(), ev->y()); webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; @@ -1506,7 +1506,7 @@ blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, doub return webEvent; } -bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, QWheelEvent *ev, double dpiScale) +bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, QWheelEvent *ev) { if (webEventTypeForEvent(ev) != webEvent.GetType()) return false; @@ -1520,7 +1520,7 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, #endif webEvent.SetTimeStamp(base::TimeTicks::Now()); - webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); + webEvent.SetPositionInWidget(ev->x(), ev->y()); webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); webEvent.wheel_ticks_x += static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h index 4b22de7d7..526202cfb 100644 --- a/src/core/web_event_factory.h +++ b/src/core/web_event_factory.h @@ -66,17 +66,17 @@ QT_END_NAMESPACE class WebEventFactory { public: - static blink::WebMouseEvent toWebMouseEvent(QMouseEvent*, double dpiScale); - static blink::WebMouseEvent toWebMouseEvent(QHoverEvent*, double dpiScale); + static blink::WebMouseEvent toWebMouseEvent(QMouseEvent *); + static blink::WebMouseEvent toWebMouseEvent(QHoverEvent *); #ifndef QT_NO_TABLETEVENT - static blink::WebMouseEvent toWebMouseEvent(QTabletEvent*, double dpiScale); + static blink::WebMouseEvent toWebMouseEvent(QTabletEvent *); #endif static blink::WebMouseEvent toWebMouseEvent(QEvent *); #ifndef QT_NO_GESTURES - static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *, double dpiScale); + static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *); #endif - static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent*, double dpiScale); - static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent*, double dpiScale); + static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent *); + static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent *); static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*); static bool getEditCommand(QKeyEvent *event, std::string *editCommand); }; diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 32fce325b..6698a9736 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -qtHaveModule(designer):qtHaveModule(webenginewidgets): SUBDIRS += qwebengineview +qtHaveModule(designer): SUBDIRS += qwebengineview diff --git a/src/src.pro b/src/src.pro index 218cdb66d..30562686a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -14,11 +14,7 @@ core.depends = buildtools SUBDIRS += buildtools \ core \ - process \ - webengine \ - webengine_plugin \ - plugins - + process qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross_compile { SUBDIRS += qwebengine_convert_dict @@ -26,19 +22,11 @@ qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross qwebengine_convert_dict.depends = core } -qtConfig(webengine-testsupport) { - webengine_testsupport_plugin.subdir = webengine/plugin/testsupport - webengine_testsupport_plugin.target = sub-webengine-testsupport-plugin - webengine_testsupport_plugin.depends = webengine - SUBDIRS += webengine_testsupport_plugin -} - -qtConfig(webengine-ui-delegates) { - SUBDIRS += webengine/ui \ - webengine/ui2 +qtConfig(webengine-qml) { + SUBDIRS += webengine } -qtHaveModule(widgets) { - SUBDIRS += webenginewidgets +qtConfig(webengine-widgets) { + SUBDIRS += plugins webenginewidgets plugins.depends = webenginewidgets } diff --git a/src/webengine/api/qquickwebengineclientcertificateselection.cpp b/src/webengine/api/qquickwebengineclientcertificateselection.cpp new file mode 100644 index 000000000..c48a59887 --- /dev/null +++ b/src/webengine/api/qquickwebengineclientcertificateselection.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickwebengineclientcertificateselection_p.h" + +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + +#include "client_cert_select_controller.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype WebEngineClientCertificateOption + \instantiates QQuickWebEngineClientCertificateOption + \inqmlmodule QtWebEngine + \since QtWebEngine 1.9 + \brief Represents a client certificate option. + + \sa {WebEngineClientCertificateSelection::certificates} {WebEngineClientCertificateSelection.certificates} +*/ + +QQuickWebEngineClientCertificateOption::QQuickWebEngineClientCertificateOption(QQuickWebEngineClientCertificateSelection *selection, int index) + : QObject(selection), m_selection(selection), m_index(index) +{} + +/*! + \qmlproperty string WebEngineClientCertificateOption::issuer + \brief The issuer of the certificate. +*/ + +QString QQuickWebEngineClientCertificateOption::issuer() const +{ + return m_selection->d_ptr->certificates().at(m_index).issuerDisplayName(); +} + +/*! + \qmlproperty string WebEngineClientCertificateOption::subject + \brief The subject of the certificate. +*/ +QString QQuickWebEngineClientCertificateOption::subject() const +{ + return m_selection->d_ptr->certificates().at(m_index).subjectDisplayName(); +} + +/*! + \qmlproperty datetime WebEngineClientCertificateOption::effectiveDate + \brief The date and time when the certificate becomes valid. +*/ +QDateTime QQuickWebEngineClientCertificateOption::effectiveDate() const +{ + return m_selection->d_ptr->certificates().at(m_index).effectiveDate(); +} + +/*! + \qmlproperty datetime WebEngineClientCertificateOption::expiryDate + \brief The date and time when the certificate becomes invalid. +*/ +QDateTime QQuickWebEngineClientCertificateOption::expiryDate() const +{ + return m_selection->d_ptr->certificates().at(m_index).expiryDate(); +} + +/*! + \qmlproperty bool WebEngineClientCertificateOption::isSelfSigned + \brief Whether the certificate is only self-signed. +*/ +bool QQuickWebEngineClientCertificateOption::isSelfSigned() const +{ + return m_selection->d_ptr->certificates().at(m_index).isSelfSigned(); +} + +/*! + \qmlmethod void WebEngineClientCertificateOption::select() + + Selects this client certificate option. +*/ +void QQuickWebEngineClientCertificateOption::select() +{ + m_selection->select(m_index); +} + +/*! + \qmltype WebEngineClientCertificateSelection + \instantiates QQuickWebEngineClientCertificateSelection + \inqmlmodule QtWebEngine + \since QtWebEngine 1.9 + \brief Provides a selection of client certificates. + + When a web site requests an SSL client certificate, and one or more certificates + are found in the system's client certificate store, this type provides access to + the certificates to choose from, as well as a method for selecting one. + + The selection is asynchronous. If no certificate is selected and no copy of the + object is kept alive, loading will continue without a certificate. + + \sa {WebEngineView::selectClientCertificate}{WebEngineView.selectClientCertificate} +*/ + +QQuickWebEngineClientCertificateSelection::QQuickWebEngineClientCertificateSelection(QSharedPointer<ClientCertSelectController> selectController) + : QObject(), d_ptr(selectController) +{} + +int QQuickWebEngineClientCertificateSelection::certificates_count( + QQmlListProperty<QQuickWebEngineClientCertificateOption> *p) +{ + Q_ASSERT(p && p->object); + QQuickWebEngineClientCertificateSelection *d = static_cast<QQuickWebEngineClientCertificateSelection *>(p->object); + return d->m_certificates.size(); +} + +QQuickWebEngineClientCertificateOption *QQuickWebEngineClientCertificateSelection::certificates_at( + QQmlListProperty<QQuickWebEngineClientCertificateOption> *p, int idx) +{ + Q_ASSERT(p && p->object); + QQuickWebEngineClientCertificateSelection *d = static_cast<QQuickWebEngineClientCertificateSelection *>(p->object); + if (idx < 0 || idx >= d->m_certificates.size()) + return nullptr; + return d->m_certificates[idx]; +} + +/*! + \qmlproperty list<WebEngineClientCertificateOption> WebEngineClientCertificateSelection::certificates + \brief The client certificates available to choose from. +*/ + +QQmlListProperty<QQuickWebEngineClientCertificateOption> QQuickWebEngineClientCertificateSelection::certificates() +{ + if (m_certificates.empty()) { + QVector<QSslCertificate> certificates = d_ptr->certificates(); + for (int i = 0; i < certificates.count(); ++i) + m_certificates.push_back(new QQuickWebEngineClientCertificateOption(this, i)); + } + + return QQmlListProperty<QQuickWebEngineClientCertificateOption>( + this, nullptr, + certificates_count, + certificates_at); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::select(WebEngineClientCertificateOption certificate) + + Selects the client certificate \a certificate. The certificate must be one + of the offered certificates. + + \sa selectNone() +*/ +void QQuickWebEngineClientCertificateSelection::select(const QQuickWebEngineClientCertificateOption *certificate) +{ + select(certificate->m_index); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::select(int index) + + Selects the client certificate at the index \a index in the list of offered certificates. + + \sa selectNone() +*/ +void QQuickWebEngineClientCertificateSelection::select(int index) +{ + d_ptr->select(index); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::selectNone() + + Continues without using any of the offered certificates. This is the same + action as taken when destroying the last copy of this object if no + selection has been made. + + \sa select() +*/ +void QQuickWebEngineClientCertificateSelection::selectNone() +{ + d_ptr->selectNone(); +} + +/*! + \qmlproperty url WebEngineClientCertificateSelection::host + \brief The host and port of the server requesting the client certificate. +*/ +QUrl QQuickWebEngineClientCertificateSelection::host() const +{ + return d_ptr->hostAndPort(); +} + +QT_END_NAMESPACE + +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) diff --git a/src/webengine/api/qquickwebengineclientcertificateselection_p.h b/src/webengine/api/qquickwebengineclientcertificateselection_p.h new file mode 100644 index 000000000..adf8b5f7c --- /dev/null +++ b/src/webengine/api/qquickwebengineclientcertificateselection_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWEBENGINECERTSELECTION_P_H +#define QQUICKWEBENGINECERTSELECTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWebEngine/private/qtwebengineglobal_p.h> + +#include <QtCore/QDateTime> +#include <QtCore/QObject> +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtCore/QVector> +#include <QtQml/QQmlListProperty> + +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + +QT_BEGIN_NAMESPACE + +class ClientCertSelectController; +class QQuickWebEngineClientCertificateSelection; + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineClientCertificateOption : public QObject { + Q_OBJECT + Q_PROPERTY(QString issuer READ issuer CONSTANT FINAL) + Q_PROPERTY(QString subject READ subject CONSTANT FINAL) + Q_PROPERTY(QDateTime effectiveDate READ effectiveDate CONSTANT FINAL) + Q_PROPERTY(QDateTime expiryDate READ expiryDate CONSTANT FINAL) + Q_PROPERTY(bool isSelfSigned READ isSelfSigned CONSTANT FINAL) + +public: + QString issuer() const; + QString subject() const; + QDateTime effectiveDate() const; + QDateTime expiryDate() const; + bool isSelfSigned() const; + + Q_INVOKABLE void select(); + +private: + friend class QQuickWebEngineClientCertificateSelection; + QQuickWebEngineClientCertificateOption(QQuickWebEngineClientCertificateSelection *selection, int index); + + QQuickWebEngineClientCertificateSelection *m_selection; + int m_index; +}; + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineClientCertificateSelection : public QObject { + Q_OBJECT + Q_PROPERTY(QUrl host READ host CONSTANT FINAL) + Q_PROPERTY(QQmlListProperty<QQuickWebEngineClientCertificateOption> certificates READ certificates CONSTANT FINAL) + +public: + QQuickWebEngineClientCertificateSelection() = default; + + QUrl host() const; + + Q_INVOKABLE void select(int idx); + Q_INVOKABLE void select(const QQuickWebEngineClientCertificateOption *certificate); + Q_INVOKABLE void selectNone(); + QQmlListProperty<QQuickWebEngineClientCertificateOption> certificates(); + +private: + friend class QQuickWebEngineViewPrivate; + friend class QQuickWebEngineClientCertificateOption; + + static int certificates_count(QQmlListProperty<QQuickWebEngineClientCertificateOption> *p); + static QQuickWebEngineClientCertificateOption *certificates_at(QQmlListProperty<QQuickWebEngineClientCertificateOption> *p, int idx); + + explicit QQuickWebEngineClientCertificateSelection(QSharedPointer<ClientCertSelectController>); + + mutable QVector<QQuickWebEngineClientCertificateOption *> m_certificates; + QSharedPointer<ClientCertSelectController> d_ptr; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQuickWebEngineClientCertificateOption *) +Q_DECLARE_METATYPE(QQmlListProperty<QQuickWebEngineClientCertificateOption>) +Q_DECLARE_METATYPE(QQuickWebEngineClientCertificateSelection *) + +#endif + +#endif // QQUICKWEBENGINECERTSELECTION_P_H diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 73577a04c..3c4ec0595 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -46,6 +46,7 @@ #include "qquickwebenginesettings_p.h" #include "qquickwebengineview_p_p.h" #include "qwebenginecookiestore.h" +#include "qwebenginenotification.h" #include <QQmlEngine> @@ -150,6 +151,13 @@ ASSERT_ENUMS_MATCH(QQuickWebEngineDownloadItem::MimeHtmlSaveFormat, QtWebEngineC The \a download argument holds the state of the finished download instance. */ +/*! + \fn QQuickWebEngineProfile::userNotification(QWebEngineNotification *notification) + + This signal is emitted whenever there is a newly created user notification. + The \a notification argument holds the notification instance to query data and interact with. +*/ + QQuickWebEngineProfilePrivate::QQuickWebEngineProfilePrivate(ProfileAdapter *profileAdapter) : m_settings(new QQuickWebEngineSettings()) , m_profileAdapter(profileAdapter) @@ -285,6 +293,20 @@ void QQuickWebEngineProfilePrivate::downloadUpdated(const DownloadItemInfo &info } } +void QQuickWebEngineProfilePrivate::useForGlobalCertificateVerificationChanged() +{ + Q_Q(QQuickWebEngineProfile); + Q_EMIT q->useForGlobalCertificateVerificationChanged(); +} + +void QQuickWebEngineProfilePrivate::showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &controller) +{ + Q_Q(QQuickWebEngineProfile); + auto notification = new QWebEngineNotification(controller); + QQmlEngine::setObjectOwnership(notification, QQmlEngine::JavaScriptOwnership); + Q_EMIT q->userNotification(notification); +} + void QQuickWebEngineProfilePrivate::userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script) { Q_ASSERT(p && p->data); @@ -365,6 +387,14 @@ void QQuickWebEngineProfilePrivate::userScripts_clear(QQmlListProperty<QQuickWeb */ /*! + \qmlsignal WebEngineProfile::userNotification(WebEngineNotification notification) + \since QtWebEngine 1.9 + + This signal is emitted whenever there is a newly created user notification. + The \a notification argument holds the notification instance to query data and interact with. +*/ + +/*! Constructs a new profile with the parent \a parent. */ QQuickWebEngineProfile::QQuickWebEngineProfile(QObject *parent) @@ -796,6 +826,102 @@ bool QQuickWebEngineProfile::isSpellCheckEnabled() const } /*! + \property QQuickWebEngineProfile::useForGlobalCertificateVerification + \since 5.13 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +/*! + \qmlproperty bool WebEngineProfile::useForGlobalCertificateVerification + \since QtWebEngine 1.9 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +void QQuickWebEngineProfile::setUseForGlobalCertificateVerification(bool enable) +{ + Q_D(QQuickWebEngineProfile); + if (enable != d->profileAdapter()->isUsedForGlobalCertificateVerification()) { + d->profileAdapter()->setUseForGlobalCertificateVerification(enable); + emit useForGlobalCertificateVerificationChanged(); + } +} + +bool QQuickWebEngineProfile::isUsedForGlobalCertificateVerification() const +{ + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); +} + +/*! + \qmlproperty string WebEngineProfile::downloadPath + \since QtWebEngine 1.9 + + The path to the location where the downloaded files are stored. + + Overrides the default path used for download location. + + If set to an empty string, the default path is restored. + + \note By default, the download path is QStandardPaths::DownloadLocation. +*/ + +/*! + \property QQuickWebEngineProfile::downloadPath + \since QtWebEngine 1.9 + + The path to the location where the downloaded files are stored. + + Overrides the default path used for download location, setting it to \a path. + + If set to an empty string, the default path is restored. + + \note By default, the download path is QStandardPaths::DownloadLocation. +*/ + +void QQuickWebEngineProfile::setDownloadPath(const QString &path) +{ + Q_D(QQuickWebEngineProfile); + if (downloadPath() == path) + return; + d->profileAdapter()->setDownloadPath(path); + emit downloadPathChanged(); +} + +QString QQuickWebEngineProfile::downloadPath() const +{ + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->downloadPath(); +} + +/*! Returns the cookie store for this profile. */ @@ -827,40 +953,52 @@ void QQuickWebEngineProfile::clearHttpCache() d->profileAdapter()->clearHttpCache(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \sa QWebEngineUrlRequestInterceptor + */ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QQuickWebEngineProfile); + interceptor->setProperty("deprecated", true); d->profileAdapter()->setRequestInterceptor(interceptor); + qWarning("Use of deprecated not tread-safe setter, use setUrlRequestInterceptor instead."); } +#endif /*! - Returns the custom URL scheme handler register for the URL scheme \a scheme. + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor */ -const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const +void QQuickWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { - const Q_D(QQuickWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; + Q_D(QQuickWebEngineProfile); + d->profileAdapter()->setRequestInterceptor(interceptor); } -static bool checkInternalScheme(const QByteArray &scheme) + +/*! + Returns the custom URL scheme handler register for the URL scheme \a scheme. +*/ +const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -872,25 +1010,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -901,10 +1021,7 @@ void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, Q void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -915,10 +1032,7 @@ void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler * void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QQuickWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -927,12 +1041,7 @@ void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QQuickWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QQuickWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); -} - -void QQuickWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) -{ - removeUrlSchemeHandler(obj); + d->profileAdapter()->removeAllUrlSchemeHandlers(); } QQuickWebEngineSettings *QQuickWebEngineProfile::settings() const @@ -970,4 +1079,15 @@ QQmlListProperty<QQuickWebEngineScript> QQuickWebEngineProfile::userScripts() d->userScripts_clear); } +/*! + \since 5.13 + + Returns the profile's client certificate store. +*/ +QWebEngineClientCertificateStore *QQuickWebEngineProfile::clientCertificateStore() +{ + Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->clientCertificateStore(); +} + QT_END_NAMESPACE diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h index 9fc4f9eca..e6f9fb73d 100644 --- a/src/webengine/api/qquickwebengineprofile.h +++ b/src/webengine/api/qquickwebengineprofile.h @@ -54,7 +54,9 @@ class QQuickWebEngineDownloadItem; class QQuickWebEngineProfilePrivate; class QQuickWebEngineScript; class QQuickWebEngineSettings; +class QWebEngineClientCertificateStore; class QWebEngineCookieStore; +class QWebEngineNotification; class QWebEngineUrlRequestInterceptor; class QWebEngineUrlSchemeHandler; @@ -72,6 +74,12 @@ class Q_WEBENGINE_EXPORT QQuickWebEngineProfile : public QObject { Q_PROPERTY(QStringList spellCheckLanguages READ spellCheckLanguages WRITE setSpellCheckLanguages NOTIFY spellCheckLanguagesChanged FINAL REVISION 3) Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY spellCheckEnabledChanged FINAL REVISION 3) Q_PROPERTY(QQmlListProperty<QQuickWebEngineScript> userScripts READ userScripts FINAL REVISION 4) + Q_PROPERTY(bool useForGlobalCertificateVerification + READ isUsedForGlobalCertificateVerification + WRITE setUseForGlobalCertificateVerification + NOTIFY useForGlobalCertificateVerificationChanged + FINAL REVISION 5) + Q_PROPERTY(QString downloadPath READ downloadPath WRITE setDownloadPath NOTIFY downloadPathChanged FINAL REVISION 5) public: QQuickWebEngineProfile(QObject *parent = Q_NULLPTR); @@ -120,7 +128,10 @@ public: QWebEngineCookieStore *cookieStore() const; +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); const QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &) const; void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *); @@ -137,6 +148,14 @@ public: QQmlListProperty<QQuickWebEngineScript> userScripts(); + void setUseForGlobalCertificateVerification(bool b); + bool isUsedForGlobalCertificateVerification() const; + + QString downloadPath() const; + void setDownloadPath(const QString &path); + + QWebEngineClientCertificateStore *clientCertificateStore(); + static QQuickWebEngineProfile *defaultProfile(); Q_SIGNALS: @@ -151,12 +170,13 @@ Q_SIGNALS: Q_REVISION(1) void httpAcceptLanguageChanged(); Q_REVISION(3) void spellCheckLanguagesChanged(); Q_REVISION(3) void spellCheckEnabledChanged(); + Q_REVISION(5) void useForGlobalCertificateVerificationChanged(); + Q_REVISION(5) void downloadPathChanged(); void downloadRequested(QQuickWebEngineDownloadItem *download); void downloadFinished(QQuickWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); + Q_REVISION(5) void userNotification(QWebEngineNotification *notification); private: Q_DECLARE_PRIVATE(QQuickWebEngineProfile) diff --git a/src/webengine/api/qquickwebengineprofile_p.h b/src/webengine/api/qquickwebengineprofile_p.h index 41e513f4c..c6d412ab3 100644 --- a/src/webengine/api/qquickwebengineprofile_p.h +++ b/src/webengine/api/qquickwebengineprofile_p.h @@ -85,6 +85,10 @@ public: void downloadRequested(DownloadItemInfo &info) override; void downloadUpdated(const DownloadItemInfo &info) override; + void useForGlobalCertificateVerificationChanged() override; + + void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &controller) override; + // QQmlListPropertyHelpers static void userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script); static int userScripts_count(QQmlListProperty<QQuickWebEngineScript> *p); diff --git a/src/webengine/api/qquickwebenginesettings.cpp b/src/webengine/api/qquickwebenginesettings.cpp index 6e96e76cf..4f6a8c148 100644 --- a/src/webengine/api/qquickwebenginesettings.cpp +++ b/src/webengine/api/qquickwebenginesettings.cpp @@ -457,6 +457,20 @@ bool QQuickWebEngineSettings::dnsPrefetchEnabled() const } /*! + \qmlproperty bool WebEngineSettings::pdfViewerEnabled + \since QtWebEngine 1.9 + + Specifies that PDF documents will be opened in the internal PDF viewer + instead of being downloaded. + + Enabled by default. +*/ +bool QQuickWebEngineSettings::pdfViewerEnabled() const +{ + return d_ptr->testAttribute(WebEngineSettings::PDFViewerEnabled); +} + +/*! \qmlproperty string WebEngineSettings::defaultTextEncoding \since QtWebEngine 1.2 @@ -714,6 +728,14 @@ void QQuickWebEngineSettings::setDnsPrefetchEnabled(bool on) Q_EMIT dnsPrefetchEnabledChanged(); } +void QQuickWebEngineSettings::setPDFViewerEnabled(bool on) +{ + bool wasOn = d_ptr->testAttribute(WebEngineSettings::PDFViewerEnabled); + d_ptr->setAttribute(WebEngineSettings::PDFViewerEnabled, on); + if (wasOn != on) + Q_EMIT pdfViewerEnabledChanged(); +} + void QQuickWebEngineSettings::setUnknownUrlSchemePolicy(QQuickWebEngineSettings::UnknownUrlSchemePolicy policy) { WebEngineSettings::UnknownUrlSchemePolicy oldPolicy = d_ptr->unknownUrlSchemePolicy(); diff --git a/src/webengine/api/qquickwebenginesettings_p.h b/src/webengine/api/qquickwebenginesettings_p.h index 6e1aaca39..ae6703bd2 100644 --- a/src/webengine/api/qquickwebenginesettings_p.h +++ b/src/webengine/api/qquickwebenginesettings_p.h @@ -93,6 +93,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineSettings : public QObject { Q_PROPERTY(bool webRTCPublicInterfacesOnly READ webRTCPublicInterfacesOnly WRITE setWebRTCPublicInterfacesOnly NOTIFY webRTCPublicInterfacesOnlyChanged REVISION 6 FINAL) Q_PROPERTY(bool javascriptCanPaste READ javascriptCanPaste WRITE setJavascriptCanPaste NOTIFY javascriptCanPasteChanged REVISION 6 FINAL) Q_PROPERTY(bool dnsPrefetchEnabled READ dnsPrefetchEnabled WRITE setDnsPrefetchEnabled NOTIFY dnsPrefetchEnabledChanged REVISION 7 FINAL) + Q_PROPERTY(bool pdfViewerEnabled READ pdfViewerEnabled WRITE setPDFViewerEnabled NOTIFY pdfViewerEnabledChanged REVISION 8 FINAL) public: enum UnknownUrlSchemePolicy { @@ -135,6 +136,7 @@ public: bool webRTCPublicInterfacesOnly() const; bool javascriptCanPaste() const; bool dnsPrefetchEnabled() const; + bool pdfViewerEnabled() const; void setAutoLoadImages(bool on); void setJavascriptEnabled(bool on); @@ -166,6 +168,7 @@ public: void setWebRTCPublicInterfacesOnly(bool on); void setJavascriptCanPaste(bool on); void setDnsPrefetchEnabled(bool on); + void setPDFViewerEnabled(bool on); signals: void autoLoadImagesChanged(); @@ -198,6 +201,7 @@ signals: Q_REVISION(6) void webRTCPublicInterfacesOnlyChanged(); Q_REVISION(6) void javascriptCanPasteChanged(); Q_REVISION(7) void dnsPrefetchEnabledChanged(); + Q_REVISION(8) void pdfViewerEnabledChanged(); private: explicit QQuickWebEngineSettings(QQuickWebEngineSettings *parentSettings = 0); diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider.cpp b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp new file mode 100644 index 000000000..80f4727b6 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickwebenginetouchhandleprovider_p_p.h" + +// static +QString QQuickWebEngineTouchHandleProvider::identifier() +{ + return QStringLiteral("touchhandle"); +} + +// static +QUrl QQuickWebEngineTouchHandleProvider::url(int orientation) +{ + return QUrl(QStringLiteral("image://%1/%2").arg(identifier(), QString::number(orientation))); +} + +QQuickWebEngineTouchHandleProvider::QQuickWebEngineTouchHandleProvider() + : QQuickImageProvider(QQuickImageProvider::Image) +{ +} + +QQuickWebEngineTouchHandleProvider::~QQuickWebEngineTouchHandleProvider() +{ +} + +void QQuickWebEngineTouchHandleProvider::init(const QMap<int, QImage> &images) +{ + if (!m_touchHandleMap.empty()) { + Q_ASSERT(images.size() == m_touchHandleMap.size()); + return; + } + + m_touchHandleMap.unite(images); +} + +QImage QQuickWebEngineTouchHandleProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(size); + Q_UNUSED(requestedSize); + + Q_ASSERT(m_touchHandleMap.contains(id.toInt())); + return m_touchHandleMap.value(id.toInt()); +} diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h new file mode 100644 index 000000000..277436289 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H +#define QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/QQuickImageProvider> +#include <QtWebEngine/private/qtwebengineglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandleProvider : public QQuickImageProvider { +public: + static QString identifier(); + static QUrl url(int orientation); + + QQuickWebEngineTouchHandleProvider(); + ~QQuickWebEngineTouchHandleProvider(); + + void init(const QMap<int, QImage> &images); + virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + +private: + QMap<int, QImage> m_touchHandleMap; +}; + + +QT_END_NAMESPACE + +#endif // QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 53f12fa97..3290fe448 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -39,17 +39,18 @@ #include "qquickwebengineview_p.h" #include "qquickwebengineview_p_p.h" -#include "qtwebenginecoreglobal_p.h" #include "authentication_dialog_controller.h" #include "profile_adapter.h" #include "certificate_error_controller.h" #include "file_picker_controller.h" #include "javascript_dialog_controller.h" +#include "touch_selection_menu_controller.h" #include "qquickwebengineaction_p.h" #include "qquickwebengineaction_p_p.h" #include "qquickwebenginehistory_p.h" #include "qquickwebenginecertificateerror_p.h" +#include "qquickwebengineclientcertificateselection_p.h" #include "qquickwebenginecontextmenurequest_p.h" #include "qquickwebenginedialogrequests_p.h" #include "qquickwebenginefaviconprovider_p_p.h" @@ -59,6 +60,7 @@ #include "qquickwebengineprofile_p.h" #include "qquickwebenginesettings_p.h" #include "qquickwebenginescript_p.h" +#include "qquickwebenginetouchhandleprovider_p_p.h" #include "qwebenginequotarequest.h" #include "qwebengineregisterprotocolhandlerrequest.h" @@ -126,7 +128,6 @@ QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate() , m_webChannel(0) , m_webChannelWorld(0) , m_isBeingAdopted(false) - , m_dpiScale(1.0) , m_backgroundColor(Qt::white) , m_zoomFactor(1.0) , m_ui2Enabled(false) @@ -300,9 +301,17 @@ void QQuickWebEngineViewPrivate::allowCertificateError(const QSharedPointer<Cert m_certificateErrorControllers.append(errorController); } -void QQuickWebEngineViewPrivate::selectClientCert(const QSharedPointer<ClientCertSelectController> &) +void QQuickWebEngineViewPrivate::selectClientCert(const QSharedPointer<ClientCertSelectController> &controller) { - // Doing nothing will free the select-controller and perform default continue. +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + Q_Q(QQuickWebEngineView); + QQuickWebEngineClientCertificateSelection *certSelection = new QQuickWebEngineClientCertificateSelection(controller); + // mark the object for gc by creating temporary jsvalue + qmlEngine(q)->newQObject(certSelection); + Q_EMIT q->selectClientCertificate(certSelection); +#else + Q_UNUSED(controller); +#endif } void QQuickWebEngineViewPrivate::runGeolocationPermissionRequest(const QUrl &url) @@ -311,6 +320,12 @@ void QQuickWebEngineViewPrivate::runGeolocationPermissionRequest(const QUrl &url Q_EMIT q->featurePermissionRequested(url, QQuickWebEngineView::Geolocation); } +void QQuickWebEngineViewPrivate::runUserNotificationPermissionRequest(const QUrl &url) +{ + Q_Q(QQuickWebEngineView); + Q_EMIT q->featurePermissionRequested(url, QQuickWebEngineView::Notifications); +} + void QQuickWebEngineViewPrivate::showColorDialog(QSharedPointer<ColorChooserController> controller) { Q_Q(QQuickWebEngineView); @@ -405,11 +420,6 @@ QRectF QQuickWebEngineViewPrivate::viewportRect() const return QRectF(q->x(), q->y(), q->width(), q->height()); } -qreal QQuickWebEngineViewPrivate::dpiScale() const -{ - return m_dpiScale; -} - QColor QQuickWebEngineViewPrivate::backgroundColor() const { return m_backgroundColor; @@ -1130,12 +1140,12 @@ void QQuickWebEngineViewPrivate::didFindText(quint64 requestId, int matchCount) callback.call(args); } -void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { Q_Q(QQuickWebEngineView); QJSValue callback = m_callbacks.take(requestId); QJSValueList args; - args.append(qmlEngine(q)->toScriptValue(result)); + args.append(qmlEngine(q)->toScriptValue(*(result.data()))); callback.call(args); } @@ -1203,6 +1213,39 @@ void QQuickWebEngineViewPrivate::setToolTip(const QString &toolTipText) ui()->showToolTip(toolTipText); } +QtWebEngineCore::TouchHandleDrawableClient *QQuickWebEngineViewPrivate::createTouchHandle(const QMap<int, QImage> &images) +{ + return new QQuickWebEngineTouchHandle(ui(), images); +} + +void QQuickWebEngineViewPrivate::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &selectionBounds, const QSize &handleSize) +{ + Q_UNUSED(handleSize); + + const int kSpacingBetweenButtons = 2; + const int kMenuButtonMinWidth = 63; + const int kMenuButtonMinHeight = 38; + + int buttonCount = menuController->buttonCount(); + if (buttonCount == 1) { + menuController->runContextMenu(); + return; + } + + int width = (kSpacingBetweenButtons * (buttonCount + 1)) + (kMenuButtonMinWidth * buttonCount); + int height = kMenuButtonMinHeight + kSpacingBetweenButtons; + int x = (selectionBounds.x() + selectionBounds.x() + selectionBounds.width() - width) / 2; + int y = selectionBounds.y() - height - 2; + + QRect bounds(x, y, width, height); + ui()->showTouchSelectionMenu(menuController, bounds, kSpacingBetweenButtons); +} + +void QQuickWebEngineViewPrivate::hideTouchSelectionMenu() +{ + ui()->hideTouchSelectionMenu(); +} + bool QQuickWebEngineView::isLoading() const { Q_D(const QQuickWebEngineView); @@ -1513,6 +1556,9 @@ void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQu WebContentsAdapterClient::MediaDesktopAudioCapture | WebContentsAdapterClient::MediaDesktopVideoCapture)); break; + case Notifications: + d_ptr->adapter->runUserNotificationRequestCallback(securityOrigin, granted); + break; default: Q_UNREACHABLE(); } @@ -2277,5 +2323,43 @@ bool QQuickContextMenuBuilder::isMenuItemEnabled(ContextMenuItem menuItem) Q_UNREACHABLE(); } + +QQuickWebEngineTouchHandle::QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images) +{ + Q_ASSERT(ui); + m_item.reset(ui->createTouchHandle()); + + QQmlEngine *engine = qmlEngine(m_item.data()); + Q_ASSERT(engine); + QQuickWebEngineTouchHandleProvider *touchHandleProvider = + static_cast<QQuickWebEngineTouchHandleProvider *>(engine->imageProvider(QQuickWebEngineTouchHandleProvider::identifier())); + Q_ASSERT(touchHandleProvider); + touchHandleProvider->init(images); +} + +void QQuickWebEngineTouchHandle::setImage(int orientation) +{ + QUrl url = QQuickWebEngineTouchHandleProvider::url(orientation); + m_item->setProperty("source", url); +} + +void QQuickWebEngineTouchHandle::setBounds(const QRect &bounds) +{ + m_item->setProperty("x", bounds.x()); + m_item->setProperty("y", bounds.y()); + m_item->setProperty("width", bounds.width()); + m_item->setProperty("height", bounds.height()); +} + +void QQuickWebEngineTouchHandle::setVisible(bool visible) +{ + m_item->setProperty("visible", visible); +} + +void QQuickWebEngineTouchHandle::setOpacity(float opacity) +{ + m_item->setProperty("opacity", opacity); +} + QT_END_NAMESPACE diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index ae92b6df0..c851dcb8d 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -51,12 +51,11 @@ // We mean it. // -#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QtWebEngine/private/qtwebengineglobal_p.h> -#include "qquickwebenginescript.h" #include <QQuickItem> #include <QtGui/qcolor.h> +#include "qquickwebenginescript.h" QT_BEGIN_NAMESPACE @@ -65,6 +64,7 @@ class QQuickContextMenuBuilder; class QQuickWebEngineAction; class QQuickWebEngineAuthenticationDialogRequest; class QQuickWebEngineCertificateError; +class QQuickWebEngineClientCertificateSelection; class QQuickWebEngineColorDialogRequest; class QQuickWebEngineContextMenuRequest; class QQuickWebEngineFaviconProvider; @@ -104,7 +104,7 @@ private: const bool m_toggleOn; }; -#define LATEST_WEBENGINEVIEW_REVISION 7 +#define LATEST_WEBENGINEVIEW_REVISION 9 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem { Q_OBJECT @@ -209,7 +209,8 @@ public: MediaAudioVideoCapture, Geolocation, DesktopVideoCapture, - DesktopAudioVideoCapture + DesktopAudioVideoCapture, + Notifications, }; Q_ENUM(Feature) @@ -550,6 +551,7 @@ Q_SIGNALS: Q_REVISION(7) void devToolsViewChanged(); Q_REVISION(7) void registerProtocolHandlerRequested(const QWebEngineRegisterProtocolHandlerRequest &request); Q_REVISION(8) void printRequested(); + Q_REVISION(9) void selectClientCertificate(QQuickWebEngineClientCertificateSelection *clientCertSelection); #if QT_CONFIG(webengine_testsupport) void testSupportChanged(); diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index bc7a05b67..e625e0ccc 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -53,6 +53,7 @@ #include "qquickwebengineview_p.h" #include "render_view_context_menu_qt.h" +#include "touch_handle_drawable_client.h" #include "web_contents_adapter_client.h" #include <QPointer> @@ -64,6 +65,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegateQuick; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class UIDelegatesManager; class WebContentsAdapter; } @@ -76,6 +79,7 @@ class QQuickWebEngineContextMenuRequest; class QQuickWebEngineSettings; class QQuickWebEngineFaviconProvider; class QQuickWebEngineProfilePrivate; +class QQuickWebEngineTouchHandleProvider; QQuickWebEngineView::WebAction editorActionForKeyEvent(QKeyEvent* event); @@ -105,7 +109,6 @@ public: void selectionChanged() override { } void recentlyAudibleChanged(bool recentlyAudible) override; QRectF viewportRect() const override; - qreal dpiScale() const override; QColor backgroundColor() const override; void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) override; void loadCommitted() override; @@ -128,7 +131,7 @@ public: void didFetchDocumentMarkup(quint64, const QString&) override { } void didFetchDocumentInnerText(quint64, const QString&) override { } void didFindText(quint64, int) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; @@ -142,6 +145,7 @@ public: void allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController) override; void selectClientCert(const QSharedPointer<ClientCertSelectController> &selectController) override; void runGeolocationPermissionRequest(QUrl const&) override; + void runUserNotificationPermissionRequest(QUrl const&) override; void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) override; void requestGeometryChange(const QRect &geometry, const QRect &frameGeometry) override; void updateScrollPosition(const QPointF &position) override; @@ -152,6 +156,9 @@ public: bool supportsDragging() const override; bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) override; + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override; + void hideTouchSelectionMenu() override; const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::QmlClient; } @@ -211,7 +218,6 @@ public: private: QScopedPointer<QtWebEngineCore::UIDelegatesManager> m_uIDelegatesManager; QList<QQuickWebEngineScript *> m_userScripts; - qreal m_dpiScale; QColor m_backgroundColor; qreal m_zoomFactor; bool m_ui2Enabled; @@ -253,6 +259,19 @@ private: QObject *m_menu; }; +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandle : public QtWebEngineCore::TouchHandleDrawableClient { +public: + QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images); + + void setImage(int orientation) override; + void setBounds(const QRect &bounds) override; + void setVisible(bool visible) override; + void setOpacity(float opacity) override; + +private: + QScopedPointer<QQuickItem> m_item; +}; + QT_END_NAMESPACE #endif // QQUICKWEBENGINEVIEW_P_P_H diff --git a/src/webengine/api/qtwebengineglobal.h b/src/webengine/api/qtwebengineglobal.h index 2d83be674..c2b33778a 100644 --- a/src/webengine/api/qtwebengineglobal.h +++ b/src/webengine/api/qtwebengineglobal.h @@ -41,6 +41,7 @@ #define QTWEBENGINEGLOBAL_H #include <QtCore/qglobal.h> +#include <QtWebEngine/qtwebengine-config.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/api/qtwebengineglobal_p.h b/src/webengine/api/qtwebengineglobal_p.h index 7058bef09..2d30f75b0 100644 --- a/src/webengine/api/qtwebengineglobal_p.h +++ b/src/webengine/api/qtwebengineglobal_p.h @@ -51,7 +51,9 @@ // We mean it. // -#include "qtwebengineglobal.h" +#include <QtWebEngine/qtwebengineglobal.h> +#include <QtCore/private/qglobal_p.h> +#include <QtWebEngine/private/qtwebengine-config_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/configure.json b/src/webengine/configure.json new file mode 100644 index 000000000..ec5ad34d9 --- /dev/null +++ b/src/webengine/configure.json @@ -0,0 +1,29 @@ +{ + "module": "webengine", + "depends": [ + "webenginecore-private" + ], + "features": { + "webengine-ui-delegates": { + "label": "UI Delegates", + "section": "WebEngine", + "output": [ "privateFeature" ] + }, + "webengine-testsupport": { + "label": "Test Support", + "autoDetect": "features.private_tests || call.isTestsInBuildParts", + "output": [ "privateFeature" ] + } + }, + "summary": [ + { + "section": "Qt WebEngineQml", + "condition": "features.webengine-qml", + "entries": [ + "webengine-ui-delegates", + "webengine-testsupport" + ] + } + ] +} + diff --git a/src/webengine/doc/src/external-resources.qdoc b/src/webengine/doc/src/external-resources.qdoc index 7ff6eea6b..e4fe9bba6 100644 --- a/src/webengine/doc/src/external-resources.qdoc +++ b/src/webengine/doc/src/external-resources.qdoc @@ -146,3 +146,8 @@ \externalpage https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler \title registerProtocolHandler */ + +/*! + \externalpage https://www.w3.org/TR/notifications + \title Web Notifications API +*/ diff --git a/src/webengine/doc/src/qtwebengine-features.qdoc b/src/webengine/doc/src/qtwebengine-features.qdoc index 1dce17e08..f53c118a8 100644 --- a/src/webengine/doc/src/qtwebengine-features.qdoc +++ b/src/webengine/doc/src/qtwebengine-features.qdoc @@ -50,6 +50,7 @@ \li \l{Touch} \li \l{View Source} \li \l{WebRTC} + \li \l{Web Notifications} \endlist \section1 Audio and Video Codecs @@ -511,4 +512,13 @@ This feature can be tested by setting up a webcam or microphone and then opening \c https://test.webrtc.org/ in \l{WebEngine Widgets Simple Browser Example}{Simple Browser} or \l{WebEngine Quick Nano Browser}{Nano Browser}. + + \section1 Web Notifications + + Qt WebEngine supports JavaScript \l{Web Notifications API}. + The application has to explicitly allow the feature by using + QWebEnginePage::Notifications or \l{WebEngineView::Feature} + {WebEngineView.Notifications}. + + Support for this feature was added in Qt 5.13.0. */ diff --git a/src/webengine/doc/src/qtwebengine-overview.qdoc b/src/webengine/doc/src/qtwebengine-overview.qdoc index d83a65bdb..58c0708cc 100644 --- a/src/webengine/doc/src/qtwebengine-overview.qdoc +++ b/src/webengine/doc/src/qtwebengine-overview.qdoc @@ -238,14 +238,18 @@ are automatically retrieved from the system. Settings from an installed QNetworkProxyFactory will be ignored, though. - Not all properties of QNetworkProxy are supported by Qt WebEngine. That is, - QNetworkProxy::type(), QNetworkProxy::hostName() and QNetworkProxy::port() are taken into - account. All other proxy settings such as QNetworkProxy::rawHeader(), QNetworkProxy::user(), or - QNetworkProxy::password() are ignored. + In case QNetworkProxy::user() and QNetworkProxy::password() are set, these credentials + will be automatically used for proxy authentication. It is up to the user to provide valid + credentials, since there is no error handling callback. - If a proxy requires authentication, QWebEnginePage::proxyAuthenticationRequired is emitted. + If no credentials are set with QNetworkProxy, but the proxy requires authentication, + QWebEnginePage::proxyAuthenticationRequired is emitted. For Qt Quick, a dialog is shown. + Not all properties of QNetworkProxy are supported by Qt WebEngine. That is, + QNetworkProxy::type(), QNetworkProxy::hostName() and QNetworkProxy::port() are taken into + account. All other proxy settings such as QNetworkProxy::rawHeader() are ignored. + \section1 High DPI Support To support High DPI devices, it is recommended that the application attribute diff --git a/src/webengine/doc/src/qtwebengine-qmlmodule.qdoc b/src/webengine/doc/src/qtwebengine-qmlmodule.qdoc index 5e172087d..540d74035 100644 --- a/src/webengine/doc/src/qtwebengine-qmlmodule.qdoc +++ b/src/webengine/doc/src/qtwebengine-qmlmodule.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \qmlmodule QtWebEngine 1.8 + \qmlmodule QtWebEngine 1.9 \title Qt WebEngine QML Types \brief Provides QML types for rendering web content within a QML application. \ingroup qtwebengine-modules @@ -36,7 +36,7 @@ your .qml file: \badcode - import QtWebEngine 1.8 + import QtWebEngine 1.9 \endcode To link against the module, add the following QT variable to your qmake .pro diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc index 0e25e16f3..a6f2af39e 100644 --- a/src/webengine/doc/src/webengineview_lgpl.qdoc +++ b/src/webengine/doc/src/webengineview_lgpl.qdoc @@ -858,6 +858,8 @@ (Added in Qt 5.10) \value DesktopAudioVideoCapture Both audio and video output capture. (Added in Qt 5.10) + \value WebEnginView.Notifications + Web notifications for the end-user. \sa featurePermissionRequested(), grantFeaturePermission() */ @@ -1475,3 +1477,19 @@ \sa printToPdf */ + +/*! + \qmlsignal WebEngineView::selectClientCertificate(WebEngineClientCertificateSelection clientCertificateSelection) + \since QtWebEngine 1.9 + + This signal is emitted when a web site requests an SSL client certificate, and one or more were + found in the system's client certificate store. + + Handling the signal is asynchronous, and loading will be waiting until a certificate is selected, + or the last copy of \a clientCertificateSelection is destroyed. + + If the signal is not handled, \a clientCertificateSelection is automatically destroyed, and loading + will continue without a client certificate. + + \sa WebEngineClientCertificateSelection +*/ diff --git a/src/webengine/module.pro b/src/webengine/module.pro new file mode 100644 index 000000000..49a1086b2 --- /dev/null +++ b/src/webengine/module.pro @@ -0,0 +1,94 @@ +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) +QT_FOR_CONFIG += webengine-private + +TARGET = QtWebEngine +MODULE = webengine + +# For our export macros +DEFINES += QT_BUILD_WEBENGINE_LIB + +QT += qml quick webenginecore +QT_PRIVATE += quick-private gui-private core-private webenginecore-private + +QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf + +INCLUDEPATH += $$PWD api ../core ../core/api + +SOURCES = \ + api/qquickwebengineaction.cpp \ + api/qquickwebenginecertificateerror.cpp \ + api/qquickwebengineclientcertificateselection.cpp \ + api/qquickwebenginecontextmenurequest.cpp \ + api/qquickwebenginedialogrequests.cpp \ + api/qquickwebenginedownloaditem.cpp \ + api/qquickwebenginehistory.cpp \ + api/qquickwebenginefaviconprovider.cpp \ + api/qquickwebengineloadrequest.cpp \ + api/qquickwebenginenavigationrequest.cpp \ + api/qquickwebenginenewviewrequest.cpp \ + api/qquickwebengineprofile.cpp \ + api/qquickwebenginescript.cpp \ + api/qquickwebenginesettings.cpp \ + api/qquickwebenginesingleton.cpp \ + api/qquickwebenginetouchhandleprovider.cpp \ + api/qquickwebengineview.cpp \ + api/qtwebengineglobal.cpp \ + render_widget_host_view_qt_delegate_quick.cpp \ + render_widget_host_view_qt_delegate_quickwindow.cpp \ + ui_delegates_manager.cpp + +HEADERS = \ + api/qtwebengineglobal.h \ + api/qtwebengineglobal_p.h \ + api/qquickwebengineaction_p.h \ + api/qquickwebengineaction_p_p.h \ + api/qquickwebenginecertificateerror_p.h \ + api/qquickwebengineclientcertificateselection_p.h \ + api/qquickwebenginecontextmenurequest_p.h \ + api/qquickwebenginedialogrequests_p.h \ + api/qquickwebenginedownloaditem_p.h \ + api/qquickwebenginedownloaditem_p_p.h \ + api/qquickwebenginehistory_p.h \ + api/qquickwebenginefaviconprovider_p_p.h \ + api/qquickwebengineloadrequest_p.h \ + api/qquickwebenginenavigationrequest_p.h \ + api/qquickwebenginenewviewrequest_p.h \ + api/qquickwebengineprofile.h \ + api/qquickwebengineprofile_p.h \ + api/qquickwebenginescript.h \ + api/qquickwebenginescript_p.h \ + api/qquickwebenginesettings_p.h \ + api/qquickwebenginesingleton_p.h \ + api/qquickwebenginetouchhandleprovider_p_p.h \ + api/qquickwebengineview_p.h \ + api/qquickwebengineview_p_p.h \ + render_widget_host_view_qt_delegate_quick.h \ + render_widget_host_view_qt_delegate_quickwindow.h \ + ui_delegates_manager.h + +qtConfig(webengine-testsupport) { + QT_PRIVATE += testlib + SOURCES += api/qquickwebenginetestsupport.cpp + HEADERS += api/qquickwebenginetestsupport_p.h +} + +!build_pass { + python = $$pythonPathForShell() + chromium_attributions.commands = \ + cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ + $$python chromium/tools/licenses.py \ + --file-template ../../tools/about_credits.tmpl \ + --entry-template ../../tools/about_credits_entry.tmpl credits \ + $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) + chromium_attributions.CONFIG += phony + + QMAKE_EXTRA_TARGETS += chromium_attributions + + prepare_docs { + prepare_docs.depends += chromium_attributions + } else { + html_docs.depends += chromium_attributions + } +} + +load(qt_module) diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index 84a12c930..381837e08 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -40,22 +40,24 @@ #include <QtQml/qqmlextensionplugin.h> #include <QtWebEngine/QQuickWebEngineProfile> -#include "qquickwebenginecertificateerror_p.h" -#include "qquickwebenginecontextmenurequest_p.h" -#include "qquickwebenginedialogrequests_p.h" -#include "qquickwebenginedownloaditem_p.h" -#include "qquickwebenginehistory_p.h" -#include "qquickwebenginefaviconprovider_p_p.h" -#include "qquickwebengineloadrequest_p.h" -#include "qquickwebenginenavigationrequest_p.h" -#include "qquickwebenginenewviewrequest_p.h" -#include "qquickwebenginesettings_p.h" -#include "qquickwebenginesingleton_p.h" -#include "qquickwebengineview_p.h" -#include "qquickwebengineaction_p.h" -#include "qwebenginequotarequest.h" -#include "qwebengineregisterprotocolhandlerrequest.h" -#include "qtwebengineversion.h" +#include <QtWebEngine/private/qquickwebenginecertificateerror_p.h> +#include <QtWebEngine/private/qquickwebengineclientcertificateselection_p.h> +#include <QtWebEngine/private/qquickwebenginecontextmenurequest_p.h> +#include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> +#include <QtWebEngine/private/qquickwebenginedownloaditem_p.h> +#include <QtWebEngine/private/qquickwebenginehistory_p.h> +#include <QtWebEngine/private/qquickwebenginefaviconprovider_p_p.h> +#include <QtWebEngine/private/qquickwebengineloadrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenavigationrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenewviewrequest_p.h> +#include <QtWebEngine/private/qquickwebenginesettings_p.h> +#include <QtWebEngine/private/qquickwebenginesingleton_p.h> +#include <QtWebEngine/private/qquickwebenginetouchhandleprovider_p_p.h> +#include <QtWebEngine/private/qquickwebengineview_p.h> +#include <QtWebEngine/private/qquickwebengineaction_p.h> +#include <QtWebEngineCore/qwebenginenotification.h> +#include <QtWebEngineCore/qwebenginequotarequest.h> +#include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h> QT_BEGIN_NAMESPACE @@ -73,6 +75,7 @@ public: { Q_UNUSED(uri); engine->addImageProvider(QQuickWebEngineFaviconProvider::identifier(), new QQuickWebEngineFaviconProvider); + engine->addImageProvider(QQuickWebEngineTouchHandleProvider::identifier(), new QQuickWebEngineTouchHandleProvider); } void registerTypes(const char *uri) override @@ -91,11 +94,13 @@ public: qmlRegisterType<QQuickWebEngineView, 6>(uri, 1, 6, "WebEngineView"); qmlRegisterType<QQuickWebEngineView, 7>(uri, 1, 7, "WebEngineView"); qmlRegisterType<QQuickWebEngineView, 8>(uri, 1, 8, "WebEngineView"); + qmlRegisterType<QQuickWebEngineView, 9>(uri, 1, 9, "WebEngineView"); qmlRegisterType<QQuickWebEngineProfile>(uri, 1, 1, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 1>(uri, 1, 2, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 2>(uri, 1, 3, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 3>(uri, 1, 4, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 4>(uri, 1, 5, "WebEngineProfile"); + qmlRegisterType<QQuickWebEngineProfile, 5>(uri, 1, 9, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineScript>(uri, 1, 1, "WebEngineScript"); qmlRegisterUncreatableType<QQuickWebEngineCertificateError>(uri, 1, 1, "WebEngineCertificateError", msgUncreatableType("WebEngineCertificateError")); qmlRegisterUncreatableType<QQuickWebEngineDownloadItem>(uri, 1, 1, "WebEngineDownloadItem", @@ -153,6 +158,11 @@ public: qmlRegisterUncreatableType<QWebEngineRegisterProtocolHandlerRequest>(uri, 1, 7, "RegisterProtocolHandlerRequest", msgUncreatableType("RegisterProtocolHandlerRequest")); qmlRegisterUncreatableType<QQuickWebEngineAction>(uri, 1, 8, "WebEngineAction", msgUncreatableType("WebEngineAction")); + qmlRegisterUncreatableType<QQuickWebEngineClientCertificateSelection>(uri, 1, 9, "WebEngineClientCertificateSelection", + msgUncreatableType("WebEngineClientCertificateSelection")); + qmlRegisterUncreatableType<QQuickWebEngineClientCertificateOption>(uri, 1, 9, "WebEngineClientCertificateOption", + msgUncreatableType("WebEngineClientCertificateOption")); + qmlRegisterUncreatableType<QWebEngineNotification>(uri, 1, 9, "WebEngineNotification", msgUncreatableType("WebEngineNotification")); } private: diff --git a/src/webengine/plugin/plugin.pro b/src/webengine/plugin/plugin.pro index b6652fa26..0c1310de3 100644 --- a/src/webengine/plugin/plugin.pro +++ b/src/webengine/plugin/plugin.pro @@ -1,13 +1,12 @@ CXX_MODULE = qml TARGET = qtwebengineplugin TARGETPATH = QtWebEngine -IMPORT_VERSION = 1.8 +IMPORT_VERSION = 1.9 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += core-private webenginecore-private webengine-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/core/api $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api $$QTWEBENGINE_ROOT/include/QtWebEngine - SOURCES = plugin.cpp +QMAKE_QMLPLUGINDUMP_FLAGS = -defaultplatform load(qml_plugin) diff --git a/src/webengine/plugin/plugins.qmltypes b/src/webengine/plugin/plugins.qmltypes index 1f295ac57..74c710e8b 100644 --- a/src/webengine/plugin/plugins.qmltypes +++ b/src/webengine/plugin/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -defaultplatform -dependencies dependencies.json -nonrelocatable QtWebEngine 1.8' +// 'qmlplugindump -nonrelocatable -defaultplatform -dependencies dependencies.json QtWebEngine 1.9' Module { dependencies: ["QtQuick 2.8"] @@ -80,6 +80,46 @@ Module { Method { name: "rejectCertificate" } } Component { + name: "QQuickWebEngineClientCertificateOption" + prototype: "QObject" + exports: ["QtWebEngine/WebEngineClientCertificateOption 1.9"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "issuer"; type: "string"; isReadonly: true } + Property { name: "subject"; type: "string"; isReadonly: true } + Property { name: "effectiveDate"; type: "QDateTime"; isReadonly: true } + Property { name: "expiryDate"; type: "QDateTime"; isReadonly: true } + Property { name: "isSelfSigned"; type: "bool"; isReadonly: true } + Method { name: "select" } + } + Component { + name: "QQuickWebEngineClientCertificateSelection" + prototype: "QObject" + exports: ["QtWebEngine/WebEngineClientCertificateSelection 1.9"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "host"; type: "QUrl"; isReadonly: true } + Property { + name: "certificates" + type: "QQuickWebEngineClientCertificateOption" + isList: true + isReadonly: true + } + Method { + name: "select" + Parameter { name: "idx"; type: "int" } + } + Method { + name: "select" + Parameter { + name: "certificate" + type: "const QQuickWebEngineClientCertificateOption" + isPointer: true + } + } + Method { name: "selectNone" } + } + Component { name: "QQuickWebEngineColorDialogRequest" prototype: "QObject" exports: ["QtWebEngine/ColorDialogRequest 1.4"] @@ -437,9 +477,10 @@ Module { "QtWebEngine/WebEngineProfile 1.2", "QtWebEngine/WebEngineProfile 1.3", "QtWebEngine/WebEngineProfile 1.4", - "QtWebEngine/WebEngineProfile 1.5" + "QtWebEngine/WebEngineProfile 1.5", + "QtWebEngine/WebEngineProfile 1.9" ] - exportMetaObjectRevisions: [0, 1, 2, 3, 4] + exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5] Enum { name: "HttpCacheType" values: { @@ -474,9 +515,13 @@ Module { isList: true isReadonly: true } + Property { name: "useForGlobalCertificateVerification"; revision: 5; type: "bool" } + Property { name: "downloadPath"; revision: 5; type: "string" } Signal { name: "httpAcceptLanguageChanged"; revision: 1 } Signal { name: "spellCheckLanguagesChanged"; revision: 3 } Signal { name: "spellCheckEnabledChanged"; revision: 3 } + Signal { name: "useForGlobalCertificateVerificationChanged"; revision: 5 } + Signal { name: "downloadPathChanged"; revision: 5 } Signal { name: "downloadRequested" Parameter { name: "download"; type: "QQuickWebEngineDownloadItem"; isPointer: true } @@ -485,6 +530,11 @@ Module { name: "downloadFinished" Parameter { name: "download"; type: "QQuickWebEngineDownloadItem"; isPointer: true } } + Signal { + name: "userNotification" + revision: 6 + Parameter { name: "notification"; type: "QWebEngineNotification"; isPointer: true } + } Method { name: "clearHttpCache"; revision: 2 } } Component { @@ -664,9 +714,10 @@ Module { "QtWebEngine/WebEngineView 1.5", "QtWebEngine/WebEngineView 1.6", "QtWebEngine/WebEngineView 1.7", - "QtWebEngine/WebEngineView 1.8" + "QtWebEngine/WebEngineView 1.8", + "QtWebEngine/WebEngineView 1.9" ] - exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8] + exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Enum { name: "NavigationRequestAction" values: { @@ -723,7 +774,8 @@ Module { "MediaAudioVideoCapture": 2, "Geolocation": 3, "DesktopVideoCapture": 4, - "DesktopAudioVideoCapture": 5 + "DesktopAudioVideoCapture": 5, + "Notifications": 6 } } Enum { @@ -1131,6 +1183,15 @@ Module { Parameter { name: "request"; type: "QWebEngineRegisterProtocolHandlerRequest" } } Signal { name: "printRequested"; revision: 8 } + Signal { + name: "selectClientCertificate" + revision: 9 + Parameter { + name: "clientCertSelection" + type: "QQuickWebEngineClientCertificateSelection" + isPointer: true + } + } Method { name: "runJavaScript" Parameter { type: "string" } @@ -1257,6 +1318,32 @@ Module { } } Component { + name: "QWebEngineNotification" + prototype: "QObject" + exports: ["QtWebEngine/WebEngineNotification 1.9"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Direction" + values: { + "LeftToRight": 0, + "RightToLeft": 1, + "DirectionAuto": 2 + } + } + Property { name: "origin"; type: "QUrl"; isReadonly: true } + Property { name: "icon"; type: "QIcon"; isReadonly: true } + Property { name: "title"; type: "string"; isReadonly: true } + Property { name: "message"; type: "string"; isReadonly: true } + Property { name: "tag"; type: "string"; isReadonly: true } + Property { name: "language"; type: "string"; isReadonly: true } + Property { name: "direction"; type: "Direction"; isReadonly: true } + Signal { name: "closed" } + Method { name: "show" } + Method { name: "click" } + Method { name: "close" } + } + Component { name: "QWebEngineQuotaRequest" exports: ["QtWebEngine/QuotaRequest 1.7"] isCreatable: false diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp index 8aaf4c714..e756ee157 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp @@ -99,19 +99,9 @@ void RenderWidgetHostViewQtDelegateQuick::initAsPopup(const QRect &r) setVisible(true); } -QRectF RenderWidgetHostViewQtDelegateQuick::screenRect() const +QRectF RenderWidgetHostViewQtDelegateQuick::viewGeometry() const { - QPointF pos = mapToScene(QPointF(0,0)); - return QRectF(pos.x(), pos.y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateQuick::contentsRect() const -{ - QPointF scenePoint = mapToScene(QPointF(0, 0)); - QPointF screenPos; - if (window()) - screenPos = window()->mapToGlobal(scenePoint.toPoint()); - return QRectF(screenPos.x(), screenPos.y(), width(), height()); + return QRectF(mapToGlobal(QPointF(0, 0)), size()); } void RenderWidgetHostViewQtDelegateQuick::setKeyboardFocus() @@ -322,16 +312,7 @@ void RenderWidgetHostViewQtDelegateQuick::inputMethodEvent(QInputMethodEvent *ev void RenderWidgetHostViewQtDelegateQuick::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickItem::geometryChanged(newGeometry, oldGeometry); - - if (window()) { - const QPointF globalPos = QQuickItem::mapToGlobal(position()); - if (globalPos != m_lastGlobalPos) { - m_lastGlobalPos = globalPos; - m_client->windowBoundsChanged(); - } - } - - m_client->notifyResize(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateQuick::itemChange(ItemChange change, const ItemChangeData &value) @@ -347,8 +328,7 @@ void RenderWidgetHostViewQtDelegateQuick::itemChange(ItemChange change, const It if (!m_isPopup) m_windowConnections.append(connect(value.window, SIGNAL(closing(QQuickCloseEvent *)), SLOT(onHide()))); } - - m_client->windowChanged(); + m_client->visualPropertiesChanged(); } else if (change == QQuickItem::ItemVisibleHasChanged) { if (!m_isPopup && !value.boolValue) onHide(); @@ -362,9 +342,7 @@ QSGNode *RenderWidgetHostViewQtDelegateQuick::updatePaintNode(QSGNode *oldNode, void RenderWidgetHostViewQtDelegateQuick::onWindowPosChanged() { - if (window()) - m_lastGlobalPos = QQuickItem::mapToGlobal(position()); - m_client->windowBoundsChanged(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateQuick::onHide() diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h index d4d64804a..4edf37cff 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h @@ -59,8 +59,7 @@ public: ~RenderWidgetHostViewQtDelegateQuick(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override; bool hasKeyboardFocus() override; void lockMouse() override; @@ -113,7 +112,6 @@ private: RenderWidgetHostViewQtDelegateClient *m_client; QList<QMetaObject::Connection> m_windowConnections; bool m_isPopup; - QPointF m_lastGlobalPos; QQuickWebEngineView *m_view = nullptr; }; diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp index d3c88148e..3c6c743e0 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -62,14 +62,9 @@ void RenderWidgetHostViewQtDelegateQuickWindow::initAsPopup(const QRect &screenR show(); } -QRectF RenderWidgetHostViewQtDelegateQuickWindow::screenRect() const +QRectF RenderWidgetHostViewQtDelegateQuickWindow::viewGeometry() const { - return QRectF(x(), y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateQuickWindow::contentsRect() const -{ - return geometry(); + return m_realDelegate->viewGeometry(); } void RenderWidgetHostViewQtDelegateQuickWindow::show() diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h index 36e4ddd8a..039f6102a 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h @@ -56,8 +56,7 @@ public: ~RenderWidgetHostViewQtDelegateQuickWindow(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override {} bool hasKeyboardFocus() override { return false; } void lockMouse() override {} diff --git a/src/webengine/plugin/testsupport/plugin.cpp b/src/webengine/testsupport/plugin.cpp index d5c43a859..7a1e73d8b 100644 --- a/src/webengine/plugin/testsupport/plugin.cpp +++ b/src/webengine/testsupport/plugin.cpp @@ -39,7 +39,7 @@ #include <QtQml> -#include "qquickwebenginetestsupport_p.h" +#include <QtWebEngine/private/qquickwebenginetestsupport_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/plugin/testsupport/qmldir b/src/webengine/testsupport/qmldir index 7fff80251..7fff80251 100644 --- a/src/webengine/plugin/testsupport/qmldir +++ b/src/webengine/testsupport/qmldir diff --git a/src/webengine/plugin/testsupport/testsupport.pro b/src/webengine/testsupport/testsupport.pro index 2804635f8..a24796675 100644 --- a/src/webengine/plugin/testsupport/testsupport.pro +++ b/src/webengine/testsupport/testsupport.pro @@ -3,11 +3,9 @@ TARGET = qtwebenginetestsupportplugin TARGETPATH = QtWebEngine/testsupport IMPORT_VERSION = 1.0 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += webengine-private gui-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api - SOURCES = plugin.cpp load(qml_plugin) diff --git a/src/webengine/ui/TouchHandle.qml b/src/webengine/ui/TouchHandle.qml new file mode 100644 index 000000000..76a93829e --- /dev/null +++ b/src/webengine/ui/TouchHandle.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 + +Image { } diff --git a/src/webengine/ui/TouchSelectionMenu.qml b/src/webengine/ui/TouchSelectionMenu.qml new file mode 100644 index 000000000..7cf16b554 --- /dev/null +++ b/src/webengine/ui/TouchSelectionMenu.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Layouts 1.3 + +Rectangle { + id: menu + + signal cutTriggered + signal copyTriggered + signal pasteTriggered + signal contextMenuTriggered + + property bool isCutEnabled: false + property bool isCopyEnabled: false + property bool isPasteEnabled: false + + property color borderColor: "darkGray" + property color bgColor: "white" + + radius: 4 + border.color: borderColor + color: borderColor + antialiasing: true + + RowLayout { + anchors.fill: parent + spacing: parent.border.width + anchors.margins: parent.border.width + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCutEnabled + + Text { + id: cutText + anchors.centerIn: parent + text: "Cut" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + cutText.color = "white"; + } + onReleased: { + parent.color = bgColor; + cutText.color = "black"; + cutTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCopyEnabled + + Text { + id: copyText + anchors.centerIn: parent + text: "Copy" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + copyText.color = "white"; + } + onReleased: { + parent.color = bgColor; + copyText.color = "black"; + copyTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isPasteEnabled + + Text { + id: pasteText + anchors.centerIn: parent + text: "Paste" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + pasteText.color = "white"; + } + onReleased: { + parent.color = bgColor; + pasteText.color = "black"; + pasteTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + + Text { + id: contextMenuText + anchors.centerIn: parent + text: "..." + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + contextMenuText.color = "white"; + } + onReleased: { + parent.color = bgColor; + contextMenuText.color = "black"; + contextMenuTriggered(); + } + } + } + } +} diff --git a/src/webengine/ui/ui.pro b/src/webengine/ui/ui.pro index eb6bf435c..69f754e0c 100644 --- a/src/webengine/ui/ui.pro +++ b/src/webengine/ui/ui.pro @@ -13,6 +13,8 @@ QML_FILES += \ Menu.qml \ MenuItem.qml \ MenuSeparator.qml \ - ToolTip.qml + ToolTip.qml \ + TouchHandle.qml \ + TouchSelectionMenu.qml load(qml_module) diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp index 7e49bc77d..da120ab69 100644 --- a/src/webengine/ui_delegates_manager.cpp +++ b/src/webengine/ui_delegates_manager.cpp @@ -44,6 +44,7 @@ #include <color_chooser_controller.h> #include <file_picker_controller.h> #include <javascript_dialog_controller.h> +#include <touch_selection_menu_controller.h> #include <web_contents_adapter_client.h> #include <QFileInfo> @@ -54,6 +55,7 @@ #include <QCursor> #include <QList> #include <QScreen> +#include <QTimer> #include <QGuiApplication> // Uncomment for QML debugging @@ -125,6 +127,7 @@ const char *defaultPropertyName(QObject *obj) UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view) : m_view(view) , m_toolTip(nullptr) + , m_touchSelectionMenu(nullptr) FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR) { } @@ -568,6 +571,82 @@ void UIDelegatesManager::showToolTip(const QString &text) QMetaObject::invokeMethod(m_toolTip.data(), "open"); } +QQuickItem *UIDelegatesManager::createTouchHandle() +{ + if (!ensureComponentLoaded(TouchHandle)) + return nullptr; + + QQmlContext *context = qmlContext(m_view); + QObject *touchHandle = touchHandleComponent->beginCreate(context); + QQuickItem *item = qobject_cast<QQuickItem *>(touchHandle); + Q_ASSERT(item); + item->setParentItem(m_view); + touchHandleComponent->completeCreate(); + + return item; +} + +void UIDelegatesManager::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &bounds, const int spacing) +{ + if (!ensureComponentLoaded(TouchSelectionMenu)) + return; + + QQmlContext *context = qmlContext(m_view); + m_touchSelectionMenu.reset(touchSelectionMenuComponent->beginCreate(context)); + if (QQuickItem *item = qobject_cast<QQuickItem *>(m_touchSelectionMenu.data())) + item->setParentItem(m_view); + m_touchSelectionMenu->setParent(m_view); + + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("width")).write(bounds.width()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("height")).write(bounds.height()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("x")).write(bounds.x()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("y")).write(bounds.y()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("border.width")).write(spacing); + + // Cut button + bool cutEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Cut); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCutEnabled")).write(cutEnabled); + if (cutEnabled) { + QQmlProperty cutSignal(m_touchSelectionMenu.data(), QStringLiteral("onCutTriggered")); + CHECK_QML_SIGNAL_PROPERTY(cutSignal, touchSelectionMenuComponent->url()); + int cutIndex = menuController->metaObject()->indexOfSlot("cut()"); + QObject::connect(m_touchSelectionMenu.data(), cutSignal.method(), menuController, menuController->metaObject()->method(cutIndex)); + } + + // Copy button + bool copyEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Copy); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCopyEnabled")).write(copyEnabled); + if (copyEnabled) { + QQmlProperty copySignal(m_touchSelectionMenu.data(), QStringLiteral("onCopyTriggered")); + CHECK_QML_SIGNAL_PROPERTY(copySignal, touchSelectionMenuComponent->url()); + int copyIndex = menuController->metaObject()->indexOfSlot("copy()"); + QObject::connect(m_touchSelectionMenu.data(), copySignal.method(), menuController, menuController->metaObject()->method(copyIndex)); + } + + // Paste button + bool pasteEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Paste); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isPasteEnabled")).write(pasteEnabled); + if (pasteEnabled) { + QQmlProperty pasteSignal(m_touchSelectionMenu.data(), QStringLiteral("onPasteTriggered")); + CHECK_QML_SIGNAL_PROPERTY(pasteSignal, touchSelectionMenuComponent->url()); + int pasteIndex = menuController->metaObject()->indexOfSlot("paste()"); + QObject::connect(m_touchSelectionMenu.data(), pasteSignal.method(), menuController, menuController->metaObject()->method(pasteIndex)); + } + + // Context menu button + QQmlProperty contextMenuSignal(m_touchSelectionMenu.data(), QStringLiteral("onContextMenuTriggered")); + CHECK_QML_SIGNAL_PROPERTY(contextMenuSignal, touchSelectionMenuComponent->url()); + int contextMenuIndex = menuController->metaObject()->indexOfSlot("runContextMenu()"); + QObject::connect(m_touchSelectionMenu.data(), contextMenuSignal.method(), menuController, menuController->metaObject()->method(contextMenuIndex)); + + touchSelectionMenuComponent->completeCreate(); +} + +void UIDelegatesManager::hideTouchSelectionMenu() +{ + QTimer::singleShot(0, m_view, [this] { m_touchSelectionMenu.reset(); }); +} + UI2DelegatesManager::UI2DelegatesManager(QQuickWebEngineView *view) : UIDelegatesManager(view) { diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h index 18457e4ed..4b6e291b2 100644 --- a/src/webengine/ui_delegates_manager.h +++ b/src/webengine/ui_delegates_manager.h @@ -61,6 +61,8 @@ F(FilePicker, filePicker) SEPARATOR \ F(AuthenticationDialog, authenticationDialog) SEPARATOR \ F(ToolTip, toolTip) SEPARATOR \ + F(TouchHandle, touchHandle) SEPARATOR \ + F(TouchSelectionMenu, touchSelectionMenu) SEPARATOR \ #define COMMA_SEPARATOR , #define SEMICOLON_SEPARATOR ; @@ -81,6 +83,7 @@ namespace QtWebEngineCore { class AuthenticationDialogController; class JavaScriptDialogController; class FilePickerController; +class TouchSelectionMenuController; const char *defaultPropertyName(QObject *obj); @@ -110,6 +113,9 @@ public: void showFilePicker(QSharedPointer<FilePickerController>); virtual void showMenu(QObject *menu); void showToolTip(const QString &text); + QQuickItem *createTouchHandle(); + void showTouchSelectionMenu(TouchSelectionMenuController *, const QRect &, const int spacing); + void hideTouchSelectionMenu(); protected: bool ensureComponentLoaded(ComponentType); @@ -117,6 +123,7 @@ protected: QQuickWebEngineView *m_view; QScopedPointer<QObject> m_toolTip; QStringList m_importDirs; + QScopedPointer<QObject> m_touchSelectionMenu; FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR) diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro index 418ade9a8..23668229e 100644 --- a/src/webengine/webengine.pro +++ b/src/webengine/webengine.pro @@ -1,89 +1,19 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +TEMPLATE = subdirs -TARGET = QtWebEngine +qml_module.file = module.pro +qml_plugin.file = plugin/plugin.pro -# For our export macros -DEFINES += QT_BUILD_WEBENGINE_LIB +qml_plugin.depends = qml_module -QT += qml quick webenginecore -QT_PRIVATE += quick-private gui-private core-private webenginecore-private - -QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf - -INCLUDEPATH += $$PWD api ../core ../core/api - -SOURCES = \ - api/qquickwebengineaction.cpp \ - api/qquickwebenginecertificateerror.cpp \ - api/qquickwebenginecontextmenurequest.cpp \ - api/qquickwebenginedialogrequests.cpp \ - api/qquickwebenginedownloaditem.cpp \ - api/qquickwebenginehistory.cpp \ - api/qquickwebenginefaviconprovider.cpp \ - api/qquickwebengineloadrequest.cpp \ - api/qquickwebenginenavigationrequest.cpp \ - api/qquickwebenginenewviewrequest.cpp \ - api/qquickwebengineprofile.cpp \ - api/qquickwebenginescript.cpp \ - api/qquickwebenginesettings.cpp \ - api/qquickwebenginesingleton.cpp \ - api/qquickwebengineview.cpp \ - api/qtwebengineglobal.cpp \ - render_widget_host_view_qt_delegate_quick.cpp \ - render_widget_host_view_qt_delegate_quickwindow.cpp \ - ui_delegates_manager.cpp - -HEADERS = \ - api/qtwebengineglobal.h \ - api/qtwebengineglobal_p.h \ - api/qquickwebengineaction_p.h \ - api/qquickwebengineaction_p_p.h \ - api/qquickwebenginecertificateerror_p.h \ - api/qquickwebenginecontextmenurequest_p.h \ - api/qquickwebenginedialogrequests_p.h \ - api/qquickwebenginedownloaditem_p.h \ - api/qquickwebenginedownloaditem_p_p.h \ - api/qquickwebenginehistory_p.h \ - api/qquickwebenginefaviconprovider_p_p.h \ - api/qquickwebengineloadrequest_p.h \ - api/qquickwebenginenavigationrequest_p.h \ - api/qquickwebenginenewviewrequest_p.h \ - api/qquickwebengineprofile.h \ - api/qquickwebengineprofile_p.h \ - api/qquickwebenginescript.h \ - api/qquickwebenginescript_p.h \ - api/qquickwebenginesettings_p.h \ - api/qquickwebenginesingleton_p.h \ - api/qquickwebengineview_p.h \ - api/qquickwebengineview_p_p.h \ - render_widget_host_view_qt_delegate_quick.h \ - render_widget_host_view_qt_delegate_quickwindow.h \ - ui_delegates_manager.h +SUBDIRS += qml_module qml_plugin qtConfig(webengine-testsupport) { - QT_PRIVATE += testlib - SOURCES += api/qquickwebenginetestsupport.cpp - HEADERS += api/qquickwebenginetestsupport_p.h + testsupport_plugin.file = testsupport/testsupport.pro + testsupport_plugin.depends = qml_module + SUBDIRS += testsupport_plugin } -!build_pass { - python = $$pythonPathForShell() - chromium_attributions.commands = \ - cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ - $$python chromium/tools/licenses.py \ - --file-template ../../tools/about_credits.tmpl \ - --entry-template ../../tools/about_credits_entry.tmpl credits \ - $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) - chromium_attributions.CONFIG += phony - - QMAKE_EXTRA_TARGETS += chromium_attributions - - prepare_docs { - prepare_docs.depends += chromium_attributions - } else { - html_docs.depends += chromium_attributions - } +qtConfig(webengine-ui-delegates) { + SUBDIRS += ui \ + ui2 } - -load(qt_module) diff --git a/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp b/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp new file mode 100644 index 000000000..c15a80373 --- /dev/null +++ b/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwebenginenotificationpresenter_p.h" + +#include <QSystemTrayIcon> + +QT_BEGIN_NAMESPACE + +DefaultNotificationPresenter::DefaultNotificationPresenter(QObject *parent) : QObject(parent) +{ +#ifndef QT_NO_SYSTEMTRAYICON + m_systemTrayIcon = new QSystemTrayIcon(this); + connect(m_systemTrayIcon, &QSystemTrayIcon::messageClicked, this, &DefaultNotificationPresenter::messageClicked); +#endif +} + +DefaultNotificationPresenter::~DefaultNotificationPresenter() +{ +} + +void DefaultNotificationPresenter::show(const QWebEngineNotification ¬ification) +{ + if (!m_activeNotification.isNull()) + m_activeNotification.close(); + m_activeNotification = notification; +#ifndef QT_NO_SYSTEMTRAYICON + if (m_systemTrayIcon) { + m_systemTrayIcon->show(); + QIcon icon = notification.icon(); + if (!icon.isNull()) + m_systemTrayIcon->showMessage(notification.title(), notification.message(), icon); + else + m_systemTrayIcon->showMessage(notification.title(), notification.message()); + notification.show(); + connect(&m_activeNotification, &QWebEngineNotification::closed, this, &DefaultNotificationPresenter::closeNotification); + } +#endif +} + +void DefaultNotificationPresenter::messageClicked() +{ + if (!m_activeNotification.isNull()) + m_activeNotification.click(); +} + +void DefaultNotificationPresenter::closeNotification() +{ +#ifndef QT_NO_SYSTEMTRAYICON + const QWebEngineNotification *canceled = static_cast<const QWebEngineNotification *>(QObject::sender()); + if (m_systemTrayIcon && canceled->matches(m_activeNotification)) + m_systemTrayIcon->hide(); +#endif +} + +void defaultNotificationPresenter(const QWebEngineNotification ¬ification) +{ + static DefaultNotificationPresenter *presenter = nullptr; + if (!presenter) + presenter = new DefaultNotificationPresenter(); + presenter->show(notification); +} + + +QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h b/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h new file mode 100644 index 000000000..a66dbc1b2 --- /dev/null +++ b/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWEBENGINENOTIFICATIONPRESENTER_P_H +#define QWEBENGINENOTIFICATIONPRESENTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWebEngineCore/QWebEngineNotification> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QSystemTrayIcon; + +class DefaultNotificationPresenter : public QObject { + Q_OBJECT +public: + DefaultNotificationPresenter(QObject *parent = nullptr); + virtual ~DefaultNotificationPresenter(); + + void show(const QWebEngineNotification ¬ification); + +private Q_SLOTS: + void messageClicked(); + void closeNotification(); + +private: + QSystemTrayIcon *m_systemTrayIcon; + QWebEngineNotification m_activeNotification; +}; + +void defaultNotificationPresenter(const QWebEngineNotification ¬ification); + +QT_END_NAMESPACE + +#endif // QWEBENGINENOTIFICATIONPRESENTER_P_H diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index fae34ae8d..696b6723f 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -48,12 +48,13 @@ #include "file_picker_controller.h" #include "javascript_dialog_controller.h" #if QT_CONFIG(webengine_printing_and_pdf) -#include "printing/pdfium_document_wrapper_qt.h" +#include "printer_worker.h" #endif #include "qwebenginecertificateerror.h" #include "qwebenginefullscreenrequest.h" #include "qwebenginehistory.h" #include "qwebenginehistory_p.h" +#include "qwebenginenotification.h" #include "qwebengineprofile.h" #include "qwebengineprofile_p.h" #include "qwebenginequotarequest.h" @@ -62,6 +63,7 @@ #include "qwebenginesettings.h" #include "qwebengineview.h" #include "qwebengineview_p.h" +#include "user_notification_controller.h" #include "render_widget_host_view_qt_delegate_widget.h" #include "web_contents_adapter.h" #include "web_engine_settings.h" @@ -94,6 +96,7 @@ #include <QMimeData> #if QT_CONFIG(webengine_printing_and_pdf) #include <QPrinter> +#include <QThread> #endif #include <QStandardPaths> #include <QStyle> @@ -106,89 +109,6 @@ using namespace QtWebEngineCore; static const int MaxTooltipLength = 1024; -#if QT_CONFIG(webengine_printing_and_pdf) -static bool printPdfDataOnPrinter(const QByteArray& data, QPrinter& printer) -{ - if (!data.size()) { - qWarning("Failure to print on printer %ls: Print result data is empty.", - qUtf16Printable(printer.printerName())); - return false; - } - - QSize pageSize = printer.pageRect().size(); - PdfiumDocumentWrapperQt pdfiumWrapper(data.constData(), data.size(), pageSize); - - int toPage = printer.toPage(); - int fromPage = printer.fromPage(); - bool ascendingOrder = true; - - if (fromPage == 0 && toPage == 0) { - fromPage = 1; - toPage = pdfiumWrapper.pageCount(); - } - fromPage = qMax(1, fromPage); - toPage = qMin(pdfiumWrapper.pageCount(), toPage); - - if (printer.pageOrder() == QPrinter::LastPageFirst) { - qSwap(fromPage, toPage); - ascendingOrder = false; - } - - int pageCopies = 1; - int documentCopies = 1; - - if (!printer.supportsMultipleCopies()) - documentCopies = printer.copyCount(); - - if (printer.collateCopies()) { - pageCopies = documentCopies; - documentCopies = 1; - } - - QPainter painter; - if (!painter.begin(&printer)) { - qWarning("Failure to print on printer %ls: Could not open printer for painting.", - qUtf16Printable(printer.printerName())); - return false; - } - - for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { - int currentPageIndex = fromPage; - while (true) { - for (int printedPages = 0; printedPages < pageCopies; printedPages++) { - if (printer.printerState() == QPrinter::Aborted - || printer.printerState() == QPrinter::Error) - return false; - - QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); - if (currentImage.isNull()) - return false; - - // Painting operations are automatically clipped to the bounds of the drawable part of the page. - painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); - if (printedPages < pageCopies - 1) - printer.newPage(); - } - - if (currentPageIndex == toPage) - break; - - if (ascendingOrder) - currentPageIndex++; - else - currentPageIndex--; - - printer.newPage(); - } - if (printedDocuments < documentCopies - 1) - printer.newPage(); - } - painter.end(); - - return true; -} -#endif // QT_CONFIG(webengine_printing_and_pdf) - static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition) { switch (disposition) { @@ -241,6 +161,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) , webChannelWorldId(QWebEngineScript::MainWorld) , defaultAudioMuted(false) , defaultZoomFactor(1.0) + , requestInterceptor(nullptr) #if QT_CONFIG(webengine_printing_and_pdf) , currentPrinter(nullptr) #endif @@ -262,6 +183,8 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) QWebEnginePagePrivate::~QWebEnginePagePrivate() { + if (requestInterceptor) + profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); delete history; delete settings; profile->d_ptr->removeWebContentsAdapterClient(this); @@ -335,7 +258,7 @@ void QWebEnginePagePrivate::didUpdateTargetURL(const QUrl &hoveredUrl) void QWebEnginePagePrivate::selectionChanged() { Q_Q(QWebEnginePage); - Q_EMIT q->selectionChanged(); + QTimer::singleShot(0, q, &QWebEnginePage::selectionChanged); } void QWebEnginePagePrivate::recentlyAudibleChanged(bool recentlyAudible) @@ -349,11 +272,6 @@ QRectF QWebEnginePagePrivate::viewportRect() const return view ? view->rect() : QRectF(); } -qreal QWebEnginePagePrivate::dpiScale() const -{ - return 1.0; -} - QColor QWebEnginePagePrivate::backgroundColor() const { return m_backgroundColor; @@ -502,19 +420,35 @@ void QWebEnginePagePrivate::didFindText(quint64 requestId, int matchCount) m_callbacks.invoke(requestId, matchCount > 0); } -void QWebEnginePagePrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { #if QT_CONFIG(webengine_printing_and_pdf) + Q_Q(QWebEnginePage); + // If no currentPrinter is set that means that were printing to PDF only. if (!currentPrinter) { - m_callbacks.invoke(requestId, result); + if (!result.data()) + return; + m_callbacks.invoke(requestId, *(result.data())); return; } - bool printerResult = printPdfDataOnPrinter(result, *currentPrinter); + QThread *printerThread = new QThread; + QObject::connect(printerThread, &QThread::finished, printerThread, &QThread::deleteLater); + printerThread->start(); + + PrinterWorker *printerWorker = new PrinterWorker(result, currentPrinter); + QObject::connect(printerWorker, &PrinterWorker::resultReady, q, [=](bool success) { + currentPrinter = nullptr; + m_callbacks.invoke(requestId, success); + }); + + QObject::connect(printerWorker, &PrinterWorker::resultReady, printerThread, &QThread::quit); + QObject::connect(printerThread, &QThread::finished, printerWorker, &PrinterWorker::deleteLater); + + printerWorker->moveToThread(printerThread); + QMetaObject::invokeMethod(printerWorker, "print"); - currentPrinter = nullptr; - m_callbacks.invoke(requestId, printerResult); #else // we should never enter this branch, but just for safe-keeping... Q_UNUSED(result); @@ -616,6 +550,12 @@ void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegister Q_EMIT q->registerProtocolHandlerRequested(request); } +void QWebEnginePagePrivate::runUserNotificationPermissionRequest(const QUrl &securityOrigin) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::Notifications); +} + QObject *QWebEnginePagePrivate::accessibilityParentObject() { return view; @@ -1862,6 +1802,40 @@ void QWebEnginePagePrivate::printRequested() }); } +/*! + \since 5.13 + + Registers the request interceptor \a interceptor to intercept URL requests. + + The page does not take ownership of the pointer. This interceptor is called + after any interceptors on the profile, and unlike profile interceptors, is run + on the UI thread, making it thread-safer. Only URL requests from this page are + intercepted. + + To unset the request interceptor, set a \c nullptr. + + \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() +*/ + +void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEnginePage); + bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); + d->requestInterceptor = interceptor; + if (hadInterceptorChanged) { + if (interceptor) + d->profile->d_ptr->profileAdapter()->addPageRequestInterceptor(); + else + d->profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); + } +} + +void QWebEnginePagePrivate::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + if (requestInterceptor) + requestInterceptor->interceptRequest(info); +} + #if QT_CONFIG(menu) QMenu *QWebEnginePage::createStandardContextMenu() { @@ -1920,6 +1894,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine d->adapter->grantMouseLockPermission(true); break; case Notifications: + d->adapter->runUserNotificationRequestCallback(securityOrigin, true); break; } } else { // if (policy == PermissionDeniedByUser) @@ -1938,6 +1913,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine d->adapter->grantMouseLockPermission(false); break; case Notifications: + d->adapter->runUserNotificationRequestCallback(securityOrigin, false); break; } } @@ -2450,10 +2426,7 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &res It is the users responsibility to ensure the \a printer remains valid until \a resultCallback has been called. - \note The rendering of the current content into a temporary PDF document is asynchronous and does - not block the main thread. However, the subsequent rendering of PDF into \a printer runs on the - main thread and will therefore block the event loop. Moreover, printing runs on the browser - process, which is by default not sandboxed. + \note Printing runs on the browser process, which is by default not sandboxed. The \a resultCallback must take a boolean as parameter. If printing was successful, this boolean will have the value \c true, otherwise, its value will be \c false. diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 028f1a441..4956877a9 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -71,6 +71,7 @@ class QWebEngineQuotaRequest; class QWebEngineRegisterProtocolHandlerRequest; class QWebEngineScriptCollection; class QWebEngineSettings; +class QWebEngineUrlRequestInterceptor; class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { Q_OBJECT @@ -184,9 +185,7 @@ public: Q_ENUM(NavigationType) enum Feature { -#ifndef Q_QDOC Notifications = 0, -#endif Geolocation = 1, MediaAudioCapture = 2, MediaVideoCapture, @@ -303,6 +302,8 @@ public: void setDevToolsPage(QWebEnginePage *page); QWebEnginePage *devToolsPage() const; + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); + const QWebEngineContextMenuData &contextMenuData() const; Q_SIGNALS: diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index a50a1972a..0db462a0a 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -66,6 +66,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateWidget; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; } @@ -98,7 +100,6 @@ public: void selectionChanged() override; void recentlyAudibleChanged(bool recentlyAudible) override; QRectF viewportRect() const override; - qreal dpiScale() const override; QColor backgroundColor() const override; void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) override; void loadCommitted() override { } @@ -124,7 +125,7 @@ public: void didFetchDocumentMarkup(quint64 requestId, const QString& result) override; void didFetchDocumentInnerText(quint64 requestId, const QString& result) override; void didFindText(quint64 requestId, int matchCount) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; @@ -132,6 +133,7 @@ public: void releaseProfile() override; void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) override; void runGeolocationPermissionRequest(const QUrl &securityOrigin) override; + void runUserNotificationPermissionRequest(const QUrl &securityOrigin) override; void runMouseLockPermissionRequest(const QUrl &securityOrigin) override; void runQuotaRequest(QWebEngineQuotaRequest) override; void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) override; @@ -150,8 +152,12 @@ public: bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; void printRequested() override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &) override { return nullptr; } + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override { } + void hideTouchSelectionMenu() override { } const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } + void interceptRequest(QWebEngineUrlRequestInfo &) override; void widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegate *newWidget) override; QtWebEngineCore::ProfileAdapter *profileAdapter() override; @@ -194,6 +200,7 @@ public: bool defaultAudioMuted; qreal defaultZoomFactor; QTimer wasShownTimer; + QWebEngineUrlRequestInterceptor *requestInterceptor; QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *widget = nullptr; mutable QtWebEngineCore::CallbackDirectory m_callbacks; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 929c2aaa1..50de63e9a 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -43,6 +43,7 @@ #include "qwebenginecookiestore.h" #include "qwebenginedownloaditem.h" #include "qwebenginedownloaditem_p.h" +#include "qwebenginenotificationpresenter_p.h" #include "qwebenginepage.h" #include "qwebenginepage_p.h" #include "qwebenginesettings.h" @@ -140,6 +141,12 @@ using QtWebEngineCore::ProfileAdapter; Both session and persistent cookies are saved to and restored from disk. */ +void QWebEngineProfilePrivate::showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> ¬ification) +{ + if (m_notificationPresenter) + m_notificationPresenter(QWebEngineNotification(notification)); +} + /*! \fn QWebEngineProfile::downloadRequested(QWebEngineDownloadItem *download) @@ -379,6 +386,34 @@ void QWebEngineProfile::setPersistentStoragePath(const QString &path) } /*! + \since 5.13 + + The path to the location where the downloaded files are stored. + + \note By default, the download path is QStandardPaths::DownloadLocation. + + \sa setDownloadPath(), QStandardPaths::writableLocation() +*/ +QString QWebEngineProfile::downloadPath() const +{ + const Q_D(QWebEngineProfile); + return d->profileAdapter()->downloadPath(); +} + +/*! + Overrides the default path used for download location, setting it to \a path. + + If set to the null string, the default path is restored. + + \sa downloadPath() +*/ +void QWebEngineProfile::setDownloadPath(const QString &path) +{ + Q_D(QWebEngineProfile); + d->profileAdapter()->setDownloadPath(path); +} + +/*! Returns the path used for caches. By default, this is below StandardPaths::CacheLocation in a QtWebengine/StorageName specific @@ -542,19 +577,45 @@ QWebEngineCookieStore* QWebEngineProfile::cookieStore() return d->profileAdapter()->cookieStore(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \since 5.6 \sa QWebEngineUrlRequestInfo -*/ +*/ void QWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QWebEngineProfile); + if (interceptor) + interceptor->setProperty("deprecated", true); + d->profileAdapter()->setRequestInterceptor(interceptor); + if (interceptor) + qDebug("Use of deprecated not thread-safe setter, use setUrlRequestInterceptor instead."); +} +#endif +/*! + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \since 5.13 + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor +*/ + +void QWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEngineProfile); d->profileAdapter()->setRequestInterceptor(interceptor); } @@ -602,6 +663,18 @@ QWebEngineScriptCollection *QWebEngineProfile::scripts() const } /*! + Sets the function \a notificationPresenter as responsible for presenting sent notifications. + + \since 5.13 + \sa QWebEngineNotification +*/ +void QWebEngineProfile::setNotificationPresenter(std::function<void(const QWebEngineNotification &)> notificationPresenter) +{ + Q_D(QWebEngineProfile); + d->m_notificationPresenter = std::move(notificationPresenter); +} + +/*! Returns the default profile. The default profile uses the storage name "Default". @@ -613,6 +686,8 @@ QWebEngineProfile *QWebEngineProfile::defaultProfile() static QWebEngineProfile* profile = new QWebEngineProfile( new QWebEngineProfilePrivate(ProfileAdapter::createDefaultProfileAdapter()), ProfileAdapter::globalQObjectRoot()); + if (!profile->d_ptr->m_notificationPresenter) + profile->setNotificationPresenter(&defaultNotificationPresenter); return profile; } @@ -688,20 +763,7 @@ QWebEngineSettings *QWebEngineProfile::settings() const const QWebEngineUrlSchemeHandler *QWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -715,25 +777,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -746,10 +790,7 @@ void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEn void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -762,10 +803,7 @@ void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handl void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -776,12 +814,43 @@ void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); + d->profileAdapter()->removeAllUrlSchemeHandlers(); } -void QWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) +/*! + \since 5.13 + + Sets if this profile is to be used for downloading and caching when needed + during certificate verification, for instance for OCSP, CRLs, and AIA. + + Only one QWebEngineProfile can do this at a time, and it is recommended + that the profile fullfilling this role has a disk HTTP cache to avoid + needlessly re-downloading. If you set the option on a second profile, + it will be disabled on the profile it is currently set. + + Currently only affects Linux/NSS installations where it enables OCSP. + + As long as one profile has this option enabled, all other profiles will be + able to use it for their certificate verification. + + \sa isUsedForGlobalCertificateVerification(), httpCacheType() +*/ +void QWebEngineProfile::setUseForGlobalCertificateVerification(bool enabled) { - removeUrlSchemeHandler(obj); + Q_D(QWebEngineProfile); + d->profileAdapter()->setUseForGlobalCertificateVerification(enabled); +} + +/*! + \since 5.13 + + Returns \c true if this profile is currently being used for global + certificate verification. +*/ +bool QWebEngineProfile::isUsedForGlobalCertificateVerification() const +{ + Q_D(const QWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); } /*! @@ -795,4 +864,15 @@ void QWebEngineProfile::clearHttpCache() d->profileAdapter()->clearHttpCache(); } +/*! + \since 5.13 + + Returns the profile's client certificate store. +*/ +QWebEngineClientCertificateStore *QWebEngineProfile::clientCertificateStore() +{ + Q_D(QWebEngineProfile); + return d->profileAdapter()->clientCertificateStore(); +} + QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h index f9a564cd2..e3ddb594a 100644 --- a/src/webenginewidgets/api/qwebengineprofile.h +++ b/src/webenginewidgets/api/qwebengineprofile.h @@ -46,12 +46,16 @@ #include <QtCore/qscopedpointer.h> #include <QtCore/qstring.h> +#include <functional> + QT_BEGIN_NAMESPACE class QObject; class QUrl; +class QWebEngineClientCertificateStore; class QWebEngineCookieStore; class QWebEngineDownloadItem; +class QWebEngineNotification; class QWebEnginePage; class QWebEnginePagePrivate; class QWebEngineProfilePrivate; @@ -106,7 +110,10 @@ public: void setHttpCacheMaximumSize(int maxSize); QWebEngineCookieStore* cookieStore(); +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); void clearAllVisitedLinks(); void clearVisitedLinks(const QList<QUrl> &urls); @@ -128,19 +135,27 @@ public: void setSpellCheckEnabled(bool enabled); bool isSpellCheckEnabled() const; + void setUseForGlobalCertificateVerification(bool enabled = true); + bool isUsedForGlobalCertificateVerification() const; + + QString downloadPath() const; + void setDownloadPath(const QString &path); + + void setNotificationPresenter(std::function<void(const QWebEngineNotification &)> notificationPresenter); + + QWebEngineClientCertificateStore *clientCertificateStore(); + static QWebEngineProfile *defaultProfile(); Q_SIGNALS: void downloadRequested(QWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); - private: Q_DISABLE_COPY(QWebEngineProfile) Q_DECLARE_PRIVATE(QWebEngineProfile) QWebEngineProfile(QWebEngineProfilePrivate *, QObject *parent = Q_NULLPTR); + friend class QWebEnginePage; friend class QWebEnginePagePrivate; friend class QWebEngineUrlSchemeHandler; QScopedPointer<QWebEngineProfilePrivate> d_ptr; diff --git a/src/webenginewidgets/api/qwebengineprofile_p.h b/src/webenginewidgets/api/qwebengineprofile_p.h index cb43dace5..91c43cf0a 100644 --- a/src/webenginewidgets/api/qwebengineprofile_p.h +++ b/src/webenginewidgets/api/qwebengineprofile_p.h @@ -60,6 +60,8 @@ #include <QScopedPointer> #include <QSharedPointer> +#include <functional> + namespace QtWebEngineCore { class ProfileAdapter; } @@ -68,6 +70,7 @@ QT_BEGIN_NAMESPACE class QWebEngineBrowserContext; class QWebEngineProfilePrivate; +class QWebEngineNotification; class QWebEngineSettings; class QWebEngineProfilePrivate : public QtWebEngineCore::ProfileAdapterClient { @@ -86,6 +89,8 @@ public: void downloadRequested(DownloadItemInfo &info) override; void downloadUpdated(const DownloadItemInfo &info) override; + void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &) override; + void addWebContentsAdapterClient(QtWebEngineCore::WebContentsAdapterClient *adapter) override; void removeWebContentsAdapterClient(QtWebEngineCore::WebContentsAdapterClient *adapter) override; @@ -95,6 +100,7 @@ private: QPointer<QtWebEngineCore::ProfileAdapter> m_profileAdapter; QScopedPointer<QWebEngineScriptCollection> m_scriptCollection; QMap<quint32, QPointer<QWebEngineDownloadItem> > m_ongoingDownloads; + std::function<void(const QWebEngineNotification &)> m_notificationPresenter; }; QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebenginesettings.cpp b/src/webenginewidgets/api/qwebenginesettings.cpp index d91eb3f97..5c91511b5 100644 --- a/src/webenginewidgets/api/qwebenginesettings.cpp +++ b/src/webenginewidgets/api/qwebenginesettings.cpp @@ -109,7 +109,8 @@ static WebEngineSettings::Attribute toWebEngineAttribute(QWebEngineSettings::Web return WebEngineSettings::JavascriptCanPaste; case QWebEngineSettings::DnsPrefetchEnabled: return WebEngineSettings::DnsPrefetchEnabled; - + case QWebEngineSettings::PDFViewerEnabled: + return WebEngineSettings::PDFViewerEnabled; default: return WebEngineSettings::UnsupportedInCoreSettings; } diff --git a/src/webenginewidgets/api/qwebenginesettings.h b/src/webenginewidgets/api/qwebenginesettings.h index 9100e32d5..3657702d2 100644 --- a/src/webenginewidgets/api/qwebenginesettings.h +++ b/src/webenginewidgets/api/qwebenginesettings.h @@ -96,6 +96,7 @@ public: WebRTCPublicInterfacesOnly, JavascriptCanPaste, DnsPrefetchEnabled, + PDFViewerEnabled, }; enum FontSize { diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index 517fdc7d9..f9ab710d1 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -285,6 +285,8 @@ This enum describes the platform feature access categories that the user may be asked to grant or deny access to: + \value Notifications + Web notifications for the end-user. \value Geolocation Location hardware or service. \value MediaAudioCapture diff --git a/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc index 88f6e05c2..34fdcb30f 100644 --- a/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc @@ -188,7 +188,9 @@ \value DnsPrefetchEnabled Specifies whether WebEngine will try to pre-fetch DNS entries to speed up browsing. Disabled by default. (Added in Qt 5.12) - + \value EnablePDFViewer Specifies that PDF documents will be opened in the internal PDF viewer + instead of being downloaded. + Enabled by default. (Added in Qt 5.13) */ /*! diff --git a/src/webenginewidgets/printer_worker.cpp b/src/webenginewidgets/printer_worker.cpp new file mode 100644 index 000000000..3a670ad49 --- /dev/null +++ b/src/webenginewidgets/printer_worker.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "printer_worker.h" + +#include "printing/pdfium_document_wrapper_qt.h" + +#include <QPainter> +#include <QPrinter> + +namespace QtWebEngineCore { + +PrinterWorker::PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer) + : m_data(data) + , m_printer(printer) +{ +} + +PrinterWorker::~PrinterWorker() +{ +} + +void PrinterWorker::print() +{ + if (!m_data->size()) { + qWarning("Failure to print on printer %ls: Print result data is empty.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + QSize pageSize = m_printer->pageRect().size(); + PdfiumDocumentWrapperQt pdfiumWrapper(m_data->constData(), m_data->size(), pageSize); + + int toPage = m_printer->toPage(); + int fromPage = m_printer->fromPage(); + bool ascendingOrder = true; + + if (fromPage == 0 && toPage == 0) { + fromPage = 1; + toPage = pdfiumWrapper.pageCount(); + } + fromPage = qMax(1, fromPage); + toPage = qMin(pdfiumWrapper.pageCount(), toPage); + + if (m_printer->pageOrder() == QPrinter::LastPageFirst) { + qSwap(fromPage, toPage); + ascendingOrder = false; + } + + int pageCopies = 1; + int documentCopies = 1; + + if (!m_printer->supportsMultipleCopies()) + documentCopies = m_printer->copyCount(); + + if (m_printer->collateCopies()) { + pageCopies = documentCopies; + documentCopies = 1; + } + + QPainter painter; + if (!painter.begin(m_printer)) { + qWarning("Failure to print on printer %ls: Could not open printer for painting.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { + int currentPageIndex = fromPage; + while (true) { + for (int printedPages = 0; printedPages < pageCopies; printedPages++) { + if (m_printer->printerState() == QPrinter::Aborted + || m_printer->printerState() == QPrinter::Error) { + Q_EMIT resultReady(false); + return; + } + + QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); + if (currentImage.isNull()) { + Q_EMIT resultReady(false); + return; + } + + // Painting operations are automatically clipped to the bounds of the drawable part of the page. + painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); + if (printedPages < pageCopies - 1) + m_printer->newPage(); + } + + if (currentPageIndex == toPage) + break; + + if (ascendingOrder) + currentPageIndex++; + else + currentPageIndex--; + + m_printer->newPage(); + } + if (printedDocuments < documentCopies - 1) + m_printer->newPage(); + } + painter.end(); + + Q_EMIT resultReady(true); + return; +} + +} // namespace QtWebEngineCore diff --git a/src/webenginewidgets/printer_worker.h b/src/webenginewidgets/printer_worker.h new file mode 100644 index 000000000..96025c90e --- /dev/null +++ b/src/webenginewidgets/printer_worker.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PRINTER_WORKER_H +#define PRINTER_WORKER_H + +#include "qtwebenginecoreglobal_p.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QPrinter; +QT_END_NAMESPACE + +namespace QtWebEngineCore { + +class PrinterWorker : public QObject +{ + Q_OBJECT +public: + PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer); + virtual ~PrinterWorker(); + +public Q_SLOTS: + void print(); + +Q_SIGNALS: + void resultReady(bool success); + +private: + Q_DISABLE_COPY(PrinterWorker) + + QSharedPointer<QByteArray> m_data; + QPrinter *m_printer; +}; + +} // namespace QtWebEngineCore + +Q_DECLARE_METATYPE(QtWebEngineCore::PrinterWorker*) + +#endif // PRINTER_WORKER_H diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp index 55bdb3a62..951360c05 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -132,25 +132,31 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende "QSurfaceFormat before the QtGui application instance is created."); } #endif - - // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. - if (sharedFormat.profile() == QSurfaceFormat::CoreProfile) { - int major; - int minor; - QSurfaceFormat::OpenGLContextProfile profile; - + int major; + int minor; + QSurfaceFormat::OpenGLContextProfile profile; #ifdef Q_OS_MACOS - // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to - // a failed creation of QQuickWidget shared context. Thus make sure to request the - // major version specified in the defaultFormat instead. - major = defaultFormat.majorVersion(); - minor = defaultFormat.minorVersion(); - profile = defaultFormat.profile(); + // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to + // a failed creation of QQuickWidget shared context. Thus make sure to request the + // major version specified in the defaultFormat instead. + major = defaultFormat.majorVersion(); + minor = defaultFormat.minorVersion(); + profile = defaultFormat.profile(); #else - major = sharedFormat.majorVersion(); - minor = sharedFormat.minorVersion(); - profile = sharedFormat.profile(); + major = sharedFormat.majorVersion(); + minor = sharedFormat.minorVersion(); + profile = sharedFormat.profile(); +#endif + + // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. + // It covers the following cases: + // 1) Desktop OpenGL Core Profile. + // 2) Windows ANGLE OpenGL ES profile. + if (sharedFormat.profile() == QSurfaceFormat::CoreProfile +#ifdef Q_OS_WIN + || globalSharedContext->isOpenGLES() #endif + ) { format.setMajorVersion(major); format.setMinorVersion(minor); format.setProfile(profile); @@ -227,15 +233,9 @@ void RenderWidgetHostViewQtDelegateWidget::closeEvent(QCloseEvent *event) m_client->closePopup(); } -QRectF RenderWidgetHostViewQtDelegateWidget::screenRect() const +QRectF RenderWidgetHostViewQtDelegateWidget::viewGeometry() const { - return QRectF(x(), y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateWidget::contentsRect() const -{ - QPointF pos = mapToGlobal(QPoint(0, 0)); - return QRectF(pos.x(), pos.y(), width(), height()); + return QRectF(mapToGlobal(pos()), size()); } void RenderWidgetHostViewQtDelegateWidget::setKeyboardFocus() @@ -373,14 +373,7 @@ QVariant RenderWidgetHostViewQtDelegateWidget::inputMethodQuery(Qt::InputMethodQ void RenderWidgetHostViewQtDelegateWidget::resizeEvent(QResizeEvent *resizeEvent) { QQuickWidget::resizeEvent(resizeEvent); - - const QPoint globalPos = mapToGlobal(pos()); - if (globalPos != m_lastGlobalPos) { - m_lastGlobalPos = globalPos; - m_client->windowBoundsChanged(); - } - - m_client->notifyResize(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateWidget::showEvent(QShowEvent *event) @@ -396,7 +389,7 @@ void RenderWidgetHostViewQtDelegateWidget::showEvent(QShowEvent *event) m_windowConnections.append(connect(w, SIGNAL(xChanged(int)), SLOT(onWindowPosChanged()))); m_windowConnections.append(connect(w, SIGNAL(yChanged(int)), SLOT(onWindowPosChanged()))); } - m_client->windowChanged(); + m_client->visualPropertiesChanged(); m_client->notifyShown(); } @@ -492,8 +485,7 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) void RenderWidgetHostViewQtDelegateWidget::onWindowPosChanged() { - m_lastGlobalPos = mapToGlobal(pos()); - m_client->windowBoundsChanged(); + m_client->visualPropertiesChanged(); } } // namespace QtWebEngineCore diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h index c1cd90093..a736aa5cf 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h @@ -66,8 +66,7 @@ public: ~RenderWidgetHostViewQtDelegateWidget(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override; bool hasKeyboardFocus() override; void lockMouse() override; @@ -111,7 +110,6 @@ private: QScopedPointer<QQuickItem> m_rootItem; bool m_isPopup; QColor m_clearColor; - QPoint m_lastGlobalPos; QList<QMetaObject::Connection> m_windowConnections; QWebEnginePage *m_page = nullptr; QMetaObject::Connection m_parentDestroyedConnection; diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro index e61575d3a..d4fb40dc7 100644 --- a/src/webenginewidgets/webenginewidgets.pro +++ b/src/webenginewidgets/webenginewidgets.pro @@ -19,6 +19,7 @@ SOURCES = \ api/qwebenginedownloaditem.cpp \ api/qwebenginefullscreenrequest.cpp \ api/qwebenginehistory.cpp \ + api/qwebenginenotificationpresenter.cpp \ api/qwebenginepage.cpp \ api/qwebengineprofile.cpp \ api/qwebenginescript.cpp \ @@ -36,6 +37,7 @@ HEADERS = \ api/qwebenginedownloaditem_p.h \ api/qwebenginefullscreenrequest.h \ api/qwebenginehistory.h \ + api/qwebenginenotificationpresenter_p.h \ api/qwebenginepage.h \ api/qwebenginepage_p.h \ api/qwebengineprofile.h \ @@ -49,6 +51,9 @@ HEADERS = \ qtConfig(webengine-printing-and-pdf) { QT += printsupport + + SOURCES += printer_worker.cpp + HEADERS += printer_worker.h } load(qt_module) diff --git a/tests/auto/core/core.pro b/tests/auto/core/core.pro index c1b2147bd..5224edbdc 100644 --- a/tests/auto/core/core.pro +++ b/tests/auto/core/core.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS += \ + qwebengineclientcertificatestore \ qwebenginecookiestore \ qwebengineurlrequestinterceptor \ diff --git a/tests/auto/core/qwebengineclientcertificatestore/qwebengineclientcertificatestore.pro b/tests/auto/core/qwebengineclientcertificatestore/qwebengineclientcertificatestore.pro new file mode 100644 index 000000000..e99c7f493 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/qwebengineclientcertificatestore.pro @@ -0,0 +1 @@ +include(../tests.pri) diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/certificate.crt b/tests/auto/core/qwebengineclientcertificatestore/resources/certificate.crt new file mode 100644 index 000000000..1c7a4e6a1 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/certificate.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtOgAwIBAgIUR7/FZjFjCH94EgGF1VOOca1p8PUwDQYJKoZIhvcNAQEL +BQAwgYQxCzAJBgNVBAYTAkVVMREwDwYDVQQIDAhIRUxTSU5LSTERMA8GA1UEBwwI +SEVMU0lOS0kxFzAVBgNVBAoMDlRoZSBRdCBDb21wYW55MQ4wDAYDVQQDDAVxdC5p +bzEmMCQGCSqGSIb3DQEJARYXdmlqaXRoLmtpbmlAY2xheXNvbC5jb20wHhcNMTkw +MjE5MTIzNjE5WhcNMjAwMjE5MTIzNjE5WjCBhDELMAkGA1UEBhMCRVUxETAPBgNV +BAgMCEhFTFNJTktJMREwDwYDVQQHDAhIRUxTSU5LSTEXMBUGA1UECgwOVGhlIFF0 +IENvbXBhbnkxDjAMBgNVBAMMBXF0LmlvMSYwJAYJKoZIhvcNAQkBFhd2aWppdGgu +a2luaUBjbGF5c29sLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AOprCpRWsPUda98sp2iQse8bUcwaaRaWl2LqKXu9ELpeInS1dq+v8/NMx7c67N2B ++1l5dOM1CPsIZ27mIL2Y04fy58lGOtwtpJg5MnR50Vl8lG/NxoAWjc2XJYhhVHfJ +k+KOME2Z4gMGU/2UoMkN/la/sSDFtApJKhYgRwjvPdDXDNr/QxpNLmSaYwd+Bx/P +MqEEcUkk+tGHWI+XTvUuFczSGpjTZ+QdgYg4w/WjDD0z+cSOJ553EoeswRZHY35O +pqwgIBMFwoUPpXb5TfOm6fBeb8i8MbReR1NV2XXPQ2aPcmuY9Cmi8Tol+sXy0MvY +Yd16eZb0eD5oIGr9UjvfJMsCAwEAAaNTMFEwHQYDVR0OBBYEFHJG773IyFWSDBvD +tvm1fmP5UbEfMB8GA1UdIwQYMBaAFHJG773IyFWSDBvDtvm1fmP5UbEfMA8GA1Ud +EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAcaPiAufSZwrALeT1BRiBAj +TdeC2ly1EBw8BwmEhVmma9GRslZzxcTUiZ+W3aYdPOFFDXE+HyAKY2WBQ1iETpIa +oeqJKzBTzXXXnXT3Wu9BCD928SH8Srtays9yEZO1DRtSKcBrsipdH3KKu3kkAIVL +GJ4igY2+2EO+64usMROzaL0WiNexHLPCm9KkURPvGRE3GJULYHEEw15ClYryJI5A +fwVbcMKhQAQipIgrdoPe6aXfTeELD2RpRvU9nMKTelJI5czx4dYGRXVjjF21qiTr +ov7+d2rXx/133K13kmeeMzOIJkDX3QJwlcKvegygU6QfJwnkRM7ZUAXGTlt3jW0= +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/certificate1.crt b/tests/auto/core/qwebengineclientcertificatestore/resources/certificate1.crt new file mode 100644 index 000000000..cd3ee969e --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/certificate1.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4zCCAsugAwIBAgIUcEB7LtzUwdKmEY5b8yDuC/rvMLswDQYJKoZIhvcNAQEL +BQAwgYAxCzAJBgNVBAYTAklOMRIwEAYDVQQIDAlLYXJuYXRha2ExEjAQBgNVBAcM +CUJBTkdBTE9SRTEQMA4GA1UECgwHQ2xheXNvbDEUMBIGA1UEAwwLY2xheXNvbC5j +b20xITAfBgkqhkiG9w0BCQEWEnZpaml0aEBjbGF5c29sLmNvbTAeFw0xOTAyMTkx +MjM4NDhaFw0yMDAyMTkxMjM4NDhaMIGAMQswCQYDVQQGEwJJTjESMBAGA1UECAwJ +S2FybmF0YWthMRIwEAYDVQQHDAlCQU5HQUxPUkUxEDAOBgNVBAoMB0NsYXlzb2wx +FDASBgNVBAMMC2NsYXlzb2wuY29tMSEwHwYJKoZIhvcNAQkBFhJ2aWppdGhAY2xh +eXNvbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9pvbiJJnL +oe5YA6MCUryOYiTmj3PVdPpKMrBe2sdzvO0cx2NXpxWERK9QNnFaJb4fTY+j9fgv +ru1tsIH3AbnDbXfpEPSD4DmpSXiKgB2+KZhA82ZerJendxqaXaj2BgTG2Tc++Chk +0wzx/3KRSb97mgPKJK5yNwx3KQjeXAhR5Mkwmmf+ZUfoYHRu4BNneYs7qX9rEPDl +vrd3AaPTgjapz7EEnSO19BrZHgLWFsM0Kni2vux1jHq1wYRXRTaOG0MrNuvuHuNC +2j5C+N/MPcuuN9izbshrlteIi2a4526V4Hp1KX1BKAI2LgI+ufzdDEkuPs/u28J7 +tGiXjAmf4YUBAgMBAAGjUzBRMB0GA1UdDgQWBBQFfq9BP0BwBToA5U6gfNaxPDFt +3jAfBgNVHSMEGDAWgBQFfq9BP0BwBToA5U6gfNaxPDFt3jAPBgNVHRMBAf8EBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBmruPekYCAAgHig66OBEHcl2jCp+MH1dmW +amM5ET3X0mC588S4+JXCVOOh2XJ2ZSRiy0syPbGd0lG6niX5h35gsqSx9uZyXW/+ +KcTf+LsaxZNusLFtAOExnYuTeeAzofMvIGQpujMhD0p/gk8s64gXnlJZZh8S1OGV +3Me8PV/cat0nuGpLi/0CYj2lAquG0LfeoGZAp+JHAMJTl/xyK4B/KMvbZ7USK9zz +gqwLag93s4vBW0dpgu+g80GGhb0bF2ZFjD+j8I0i9OjFbTWxAiHomofQ6Qx12eVY +rfJs/kVG86cOLJarFHBadjlLz4pu1/sp97TsohFYDy7isRbOQdO2 +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey.key b/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey.key new file mode 100644 index 000000000..ff9ce14df --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDqawqUVrD1HWvf +LKdokLHvG1HMGmkWlpdi6il7vRC6XiJ0tXavr/PzTMe3OuzdgftZeXTjNQj7CGdu +5iC9mNOH8ufJRjrcLaSYOTJ0edFZfJRvzcaAFo3NlyWIYVR3yZPijjBNmeIDBlP9 +lKDJDf5Wv7EgxbQKSSoWIEcI7z3Q1wza/0MaTS5kmmMHfgcfzzKhBHFJJPrRh1iP +l071LhXM0hqY02fkHYGIOMP1oww9M/nEjieedxKHrMEWR2N+TqasICATBcKFD6V2 ++U3zpunwXm/IvDG0XkdTVdl1z0Nmj3JrmPQpovE6JfrF8tDL2GHdenmW9Hg+aCBq +/VI73yTLAgMBAAECggEAXfjLJOwbxCcnNzAPbDuVGOU6xTdRKVz1ExILzy7bpdpW +tbKJAaTnhoEhDrLwgVu6BwRJd0urbfbQgASA24CIS9d0U5tzvRAPC3biYw7yfY0T +dD5pdK+V6PTOdv2Nfvev8QJPGsy1bcD7qowKaI7ThrMRrrGSmwz5ciU+VLaTcUP/ +u8H22Wq+nZZGDtzmAgmHLZzOt79QlBDGsO4y15mss46AKan+ameI+iUjrRzSxZAO +uT+YHPezWE6M8lYSntvmDFxxhkrdHC8qDsmI+AChs+ELu30zsbbj7fdH1esS9/7q +PLfd0rHv3H3IfR7nvr0wIyX7DIUI3DhuGRvnRS8wAQKBgQD9vrzppukAIfoawlQW +XFhDFAC89nReMx7T1r68E8VU4Ng/61KeV+sP269jbPHC0K2/bzHbth4im05oZkcZ +ZaPWeKGmYCUr+lnV6148iFPeVKX4KROvsgIvJ4LVpJUNO4V5Jg2jQcVlkPN2yc7G +EssVH4ywREedtCFMui1lGJ46wQKBgQDsgFXP/EM0uijSWqZ2it/tdFeeS7F3Uu+V +2vErnP/xH8i1BkV2t8sLYeAVNW7fEmHcEx3DrZJ2yH3J3h7MivY5g5su1iXHHcRn +ux9bnb6uzKYS8PRxVnJVjSg9X9q1q6Tsh6Dia5wKHf60rX3MEi7UsJ36uKNKeC3H ++4zh5tq+iwKBgQDQjbmlbRsqkcKHxdro+gNNNp6YsRPZ7LhUsrbXzIQvDBKRDrA8 +FtTLlutjk8JtNajoo9Ld26SPGwjpw86CMEjl2QY7weArcjvuF+K/a+lK3lX/Tnuw +t2UWLqpZQEmFs7szZLRmz4MXxHhm1N2PbRjfhqvSoHwgQVHxR0BSTFaYwQKBgQDQ +8+Zdd4UtXdjZWcw/ah3En4kWS8xFH9qcbS+tl+4t7SYYgqz24hYcPWKNeoM1xfj+ +R0mB7KY+Go/AvmY51AEN+bAvJneEQFGaUKK5AFtL93Y3v/pI9NbZrXjDGSCUaE2z +yXUk92yLJ8s1eR9vIRWNR2qc7WsgTaQtT5w+rwfPVwKBgQC3udM2j59yDCoAOk3g +Myz3PU9I3gLk+yQ2yHWRjE0Cundr3zQ7PqlsITUe9xhUfj+soBsRw7yeg5/wYcVJ +F3hDmAOFb54pW912sD9CQbkhJMX6oVuSegUghPrXDhgiJ9i3TArc9xOKoER4A1s6 +N10tV3NPQprnu2m4jTBykPUJBw== +-----END PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey1.key b/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey1.key new file mode 100644 index 000000000..f4ae0d2d0 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/privatekey1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9pvbiJJnLoe5Y +A6MCUryOYiTmj3PVdPpKMrBe2sdzvO0cx2NXpxWERK9QNnFaJb4fTY+j9fgvru1t +sIH3AbnDbXfpEPSD4DmpSXiKgB2+KZhA82ZerJendxqaXaj2BgTG2Tc++Chk0wzx +/3KRSb97mgPKJK5yNwx3KQjeXAhR5Mkwmmf+ZUfoYHRu4BNneYs7qX9rEPDlvrd3 +AaPTgjapz7EEnSO19BrZHgLWFsM0Kni2vux1jHq1wYRXRTaOG0MrNuvuHuNC2j5C ++N/MPcuuN9izbshrlteIi2a4526V4Hp1KX1BKAI2LgI+ufzdDEkuPs/u28J7tGiX +jAmf4YUBAgMBAAECggEADLcnEl512UtQGaKUyVEWx00ZQEQZ9hPTvCnN3W45tS7T +jvxnUrcnICkiv/QGgY7KQnsGyG1zaG8ICDSvYgpBlDLAuWWjnzvnYcxFQrP4Somr +ZZ9sG6jiuh2oMPVaoLQimdpyc8xbmPb1hTwlSExzhCv9WQ5Ai/PY6BxeNdphzGu2 +sNXm0+BBiwX+UdoVYg9nNEzJe8x1xKIjIw4CWk61ZclLsoScjNOZE7RJ3oqXeYnw +EqBVqAJTZHYpS3e1LxsstOsZfI4kxdbnGjBydz1aHSP19tycY39kUFmjNq6oiSlv +22+B1WdZ87POXTF0ISL8l+9HIVOKBCeuDmZ+KgS7KQKBgQD6FunVLHxVtQPS2/e0 +yvtHFqSBHuzL2FMIt6vfl4VhQ6QZ5aNPhbGv1WNcb9Z3pqLPp7zRgIPz63l6yHDT +xTJEjCAseMoJIb/7ji1/RYE1eOhf3ie1BAuAxjdZ8rNVliZ2i3bt5aOUf4MOQdqE +HY3/yXqMr1qPJThbyTk4bB3V1wKBgQDCImT4UvQqheMFF9Yev6MhUljm9XOjamk/ +v54pW4De7NR2J+4dR+enEHeO0EKCE+olAXfGztChbY4ytkW5bh5BndGCiYLq6NdS +X4a1Bf1pM6y9/+QxHIQ99G+TGyfoiTmMT1eZNXZAF4MZN36Qe/Y0P9YlJmnRXDY9 +n8yM4+Tw5wKBgHGr0ITQP4NFT4Ob768BQ81AAW2e/Mp6fONNeJWcbUeUUxZiRiRz +BByDxTHSs4yW9RGlkDE5VMCdmw2v5Xib/QkjF8ye6lXUPUq9iPh8Qwpm9lf8xdeF +CyVw8t+zbMfOA5WGNnxTEFMC0jK8w/Gg7VYB4e3oFiVSKZeuko+OtNDpAoGAWFwZ +QuFW2sfnpVjND1/AGxmE91I+mOGFGAvaLA7G4DBBH6pa+Z7RtDuNf0YRD22LA97g +Km/Vi7P2peRKZfTV9p2jT1vRZ6Qk5QwvntMkHS707+G5dhT5p81Kv1T73B4hQFiY +kVZBBTjuZZrZk1+olu17RZtvTGhBE9lY28OaX38CgYEAirReFhouOZc7Ng9gCfTM +XYzIVZszQ8l8dD2fPhbYw9qSRNvST28s4lLtyRPvoKCpmlUMm5F6Xb65rvICMxc3 +DPnB9iLY6fLY7tETVeV9DEOXki3hoZQ4qkI7OIVbZQn1rA1Du95ZZEGdgE5eEMOC +SQ9g3B7NYJT3Zs8rkxKajWM= +-----END PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp new file mode 100644 index 000000000..03e68c533 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineclientcertificatestore.h> +#include <QtWebEngineWidgets/qwebengineprofile.h> + +class tst_QWebEngineClientCertificateStore : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineClientCertificateStore(); + ~tst_QWebEngineClientCertificateStore(); + +private Q_SLOTS: + void addAndListCertificates(); + void removeAndClearCertificates(); +}; + +tst_QWebEngineClientCertificateStore::tst_QWebEngineClientCertificateStore() +{ +} + +tst_QWebEngineClientCertificateStore::~tst_QWebEngineClientCertificateStore() +{ +} + +void tst_QWebEngineClientCertificateStore::addAndListCertificates() +{ + // Load QSslCertificate + QFile certFile(":/resources/certificate.crt"); + certFile.open(QIODevice::ReadOnly); + const QSslCertificate cert(certFile.readAll(), QSsl::Pem); + + // Load QSslKey + QFile keyFile(":/resources/privatekey.key"); + keyFile.open(QIODevice::ReadOnly); + const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, ""); + + // Load second QSslCertificate + QFile certFileSecond(":/resources/certificate1.crt"); + certFileSecond.open(QIODevice::ReadOnly); + const QSslCertificate certSecond(certFileSecond.readAll(), QSsl::Pem); + + // Load second QSslKey + QFile keyFileSecond(":/resources/privatekey1.key"); + keyFileSecond.open(QIODevice::ReadOnly); + const QSslKey sslKeySecond(keyFileSecond.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, ""); + + // Add certificates to in-memory store + QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(cert, sslKey); + QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(certSecond, sslKeySecond); + + QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->toList().length()); +} + +void tst_QWebEngineClientCertificateStore::removeAndClearCertificates() +{ + QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->toList().length()); + + // Remove one certificate from in-memory store + auto list = QWebEngineProfile::defaultProfile()->clientCertificateStore()->toList(); + QWebEngineProfile::defaultProfile()->clientCertificateStore()->remove(list[0].certificate); + QCOMPARE(1, QWebEngineProfile::defaultProfile()->clientCertificateStore()->toList().length()); + + // Remove all certificates in-memory store + QWebEngineProfile::defaultProfile()->clientCertificateStore()->clear(); + QCOMPARE(0, QWebEngineProfile::defaultProfile()->clientCertificateStore()->toList().length()); +} + +QTEST_MAIN(tst_QWebEngineClientCertificateStore) +#include "tst_qwebengineclientcertificatestore.moc" diff --git a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc new file mode 100644 index 000000000..db481fef6 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/"> + <file>resources/certificate.crt</file> + <file>resources/privatekey.key</file> + <file>resources/certificate1.crt</file> + <file>resources/privatekey1.key</file> + </qresource> +</RCC> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 23bf88417..5629998fd 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -51,7 +51,9 @@ private Q_SLOTS: void cleanupTestCase(); void interceptRequest(); void ipv6HostEncoding(); + void requestedUrl_data(); void requestedUrl(); + void setUrlSameUrl_data(); void setUrlSameUrl(); void firstPartyUrl(); void firstPartyUrlNestedIframes_data(); @@ -105,6 +107,7 @@ public: void interceptRequest(QWebEngineUrlRequestInfo &info) override { + QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); // Since 63 we also intercept some unrelated blob requests.. if (info.requestUrl().scheme() == QLatin1String("blob")) return; @@ -123,10 +126,7 @@ public: // Skip import documents and sandboxed documents. // See Document::SiteForCookies() in chromium/third_party/blink/renderer/core/dom/document.cc. - // - // TODO: Change this to empty URL during the next chromium update: - // https://chromium-review.googlesource.com/c/chromium/src/+/1213082/ - return requestInfo.firstPartyUrl == QUrl("data:,"); + return requestInfo.firstPartyUrl == QUrl(""); } QList<RequestInfo> getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceType type) @@ -168,7 +168,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -198,7 +198,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() // Make sure that registering an observer does not modify the request. TestRequestInterceptor observer(/* intercept */ false); - profile.setRequestInterceptor(&observer); + profile.setUrlRequestInterceptor(&observer); page.load(QUrl("qrc:///resources/__placeholder__")); QTRY_COMPARE(loadSpy.count(), 1); success = loadSpy.takeFirst().takeFirst(); @@ -231,7 +231,7 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() { QWebEngineProfile profile; LocalhostContentProvider contentProvider; - profile.setRequestInterceptor(&contentProvider); + profile.setUrlRequestInterceptor(&contentProvider); QWebEnginePage page(&profile); QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); @@ -249,14 +249,27 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() QCOMPARE(contentProvider.requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); } +void tst_QWebEngineUrlRequestInterceptor::requestedUrl_data() +{ + QTest::addColumn<bool>("interceptInPage"); + + QTest::newRow("Profile intercept") << false; + QTest::newRow("Page intercept") << true; +} + void tst_QWebEngineUrlRequestInterceptor::requestedUrl() { + QFETCH(bool, interceptInPage); + QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + if (!interceptInPage) + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); + if (interceptInPage) + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -273,19 +286,29 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); page.setUrl(QUrl("http://abcdef.abcdef")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 15000); QCOMPARE(interceptor.requestInfos.at(3).requestUrl, QUrl("http://abcdef.abcdef/")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); } +void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl_data() +{ + requestedUrl_data(); +} + void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() { + QFETCH(bool, interceptInPage); + QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + if (!interceptInPage) + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); + if (interceptInPage) + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -314,7 +337,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); @@ -348,7 +371,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -409,7 +432,7 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -427,7 +450,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); diff --git a/tests/auto/quick/dialogs/dialogs.pro b/tests/auto/quick/dialogs/dialogs.pro index e262c3814..29d509b20 100644 --- a/tests/auto/quick/dialogs/dialogs.pro +++ b/tests/auto/quick/dialogs/dialogs.pro @@ -1,5 +1,5 @@ include(../tests.pri) -QT += webengine webengine-private +QT += core-private webengine webengine-private HEADERS += \ server.h \ diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index ecc2764fd..cecea1831 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -26,7 +26,6 @@ ** ****************************************************************************/ -#include "qtwebengineglobal.h" #include "testhandler.h" #include "server.h" #include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 59973f7d0..450ee91da 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -35,11 +35,13 @@ #include <QtTest/QtTest> #include <QtWebEngine/QQuickWebEngineProfile> #include <QtWebEngine/QQuickWebEngineScript> +#include <QtWebEngineCore/QWebEngineNotification> #include <QtWebEngineCore/QWebEngineQuotaRequest> #include <QtWebEngineCore/QWebEngineRegisterProtocolHandlerRequest> #include <private/qquickwebengineview_p.h> #include <private/qquickwebengineaction_p.h> #include <private/qquickwebenginecertificateerror_p.h> +#include <private/qquickwebengineclientcertificateselection_p.h> #include <private/qquickwebenginedialogrequests_p.h> #include <private/qquickwebenginedownloaditem_p.h> #include <private/qquickwebenginehistory_p.h> @@ -60,6 +62,8 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineView::staticMetaObject << &QQuickWebEngineAction::staticMetaObject << &QQuickWebEngineCertificateError::staticMetaObject + << &QQuickWebEngineClientCertificateOption::staticMetaObject + << &QQuickWebEngineClientCertificateSelection::staticMetaObject << &QQuickWebEngineDownloadItem::staticMetaObject << &QQuickWebEngineHistory::staticMetaObject << &QQuickWebEngineHistoryListModel::staticMetaObject @@ -79,6 +83,7 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineContextMenuRequest::staticMetaObject << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject + << &QWebEngineNotification::staticMetaObject ; static QList<const char *> knownEnumNames = QList<const char *>(); @@ -86,6 +91,8 @@ static QList<const char *> knownEnumNames = QList<const char *>(); static const QStringList hardcodedTypes = QStringList() << "QJSValue" << "QQmlListProperty<QQuickWebEngineScript>" + << "QQmlListProperty<QQuickWebEngineClientCertificateOption>" + << "const QQuickWebEngineClientCertificateOption*" << "QQmlWebChannel*" // Ignore the testSupport types without making a fuss. << "QQuickWebEngineTestSupport*" @@ -303,6 +310,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.clearHttpCache() --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadItem*) --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadItem*) --> void" + << "QQuickWebEngineProfile.userNotification(QWebEngineNotification*) --> void" << "QQuickWebEngineProfile.httpAcceptLanguage --> QString" << "QQuickWebEngineProfile.httpAcceptLanguageChanged() --> void" << "QQuickWebEngineProfile.httpCacheMaximumSize --> int" @@ -585,6 +593,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.NewViewInTab --> NewViewDestination" << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" + << "QQuickWebEngineView.Notifications --> Feature" << "QQuickWebEngineView.NoWebAction --> WebAction" << "QQuickWebEngineView.NormalTerminationStatus --> RenderProcessTerminationStatus" << "QQuickWebEngineView.Note --> PrintedPageSizeId" @@ -727,6 +736,20 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineRegisterProtocolHandlerRequest.origin --> QUrl" << "QWebEngineRegisterProtocolHandlerRequest.reject() --> void" << "QWebEngineRegisterProtocolHandlerRequest.scheme --> QString" + << "QWebEngineNotification.origin --> QUrl" + << "QWebEngineNotification.icon --> QIcon" + << "QWebEngineNotification.title --> QString" + << "QWebEngineNotification.message --> QString" + << "QWebEngineNotification.tag --> QString" + << "QWebEngineNotification.language --> QString" + << "QWebEngineNotification.direction --> Direction" + << "QWebEngineNotification.LeftToRight --> Direction" + << "QWebEngineNotification.RightToLeft --> Direction" + << "QWebEngineNotification.DirectionAuto --> Direction" + << "QWebEngineNotification.show() --> void" + << "QWebEngineNotification.click() --> void" + << "QWebEngineNotification.close() --> void" + << "QWebEngineNotification.closed() --> void" ; static bool isCheckedEnum(const QByteArray &typeName) diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml index 019ebd9dc..5eb704cce 100644 --- a/tests/auto/quick/qmltests/data/tst_download.qml +++ b/tests/auto/quick/qmltests/data/tst_download.qml @@ -28,7 +28,8 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.5 +import QtWebEngine 1.9 +import Qt.labs.platform 1.0 TestWebEngineView { id: webEngineView @@ -42,6 +43,12 @@ TestWebEngineView { property var downloadState: [] property var downloadInterruptReason: null + function urlToPath(url) { + var path = url.toString() + path = path.replace(/^(file:\/{2})/,"") + return path + } + SignalSpy { id: downLoadRequestedSpy target: testDownloadProfile @@ -135,5 +142,16 @@ TestWebEngineView { tryCompare(downloadState, "1", WebEngineDownloadItem.DownloadCancelled) tryCompare(webEngineView, "downloadInterruptReason", WebEngineDownloadItem.UserCanceled) } + + function test_downloadLocation() { + var tmpPath = urlToPath(StandardPaths.writableLocation(StandardPaths.TempLocation)); + var downloadPath = urlToPath(StandardPaths.writableLocation(StandardPaths.DownloadLocation)); + + testDownloadProfile.downloadPath = tmpPath; + compare(testDownloadProfile.downloadPath, tmpPath); + + testDownloadProfile.downloadPath = downloadPath; + compare(testDownloadProfile.downloadPath, downloadPath); + } } } diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index dfcfd586f..1ec574fae 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -116,7 +116,7 @@ TestWebEngineView { webEngineView.clear() webEngineView.findText("bla", findFlags, webEngineView.findTextCallback) - tryCompare(webEngineView, "matchCount", 100) + tryCompare(webEngineView, "matchCount", 100, 20000) verify(!findFailed) } @@ -172,7 +172,7 @@ TestWebEngineView { webEngineView.clear() webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) - tryCompare(webEngineView, "matchCount", 0) + tryCompare(webEngineView, "matchCount", 0, 20000) verify(findFailed) runJavaScript("document.body.innerHTML = 'blahellobla'"); diff --git a/tests/auto/quick/qmltests/data/tst_notification.qml b/tests/auto/quick/qmltests/data/tst_notification.qml new file mode 100644 index 000000000..609a04f61 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_notification.qml @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtWebEngine 1.9 + +TestWebEngineView { + id: view + width: 320 + height: 320 + + property bool permissionRequested: false + property bool grantPermission: false + + signal consoleMessage(string message) + + SignalSpy { + id: spyRequest + target: view + signalName: 'featurePermissionRequested' + } + + onFeaturePermissionRequested: { + if (feature === WebEngineView.Notifications) { + permissionRequested = true + view.grantFeaturePermission(securityOrigin, feature, grantPermission) + } + } + + TestCase { + name: 'WebEngineNotification' + when: windowShown + + function resolverUrl(html) { + return Qt.resolvedUrl('../../../shared/data/' + html) + } + + function init() { + permissionRequested = false + spyRequest.clear() + } + + function test_request_data() { + return [ + { tag: 'grant', grant: true, permission: 'granted' }, + { tag: 'deny', grant: false, permission: 'denied' }, + ] + } + + function test_request(data) { + grantPermission = data.grant + + view.url = resolverUrl('notification.html') + verify(view.waitForLoadSucceeded()) + + view.runJavaScript('resetPermission()') + let result = {} + + view.runJavaScript('getPermission()', function (permission) { result.permission = permission }) + tryCompare(result, 'permission', 'default') + + view.runJavaScript('requestPermission()') + spyRequest.wait() + verify(permissionRequested) + compare(spyRequest.count, 1) + + view.runJavaScript('getPermission()', function (permission) { result.permission = permission }) + tryCompare(result, 'permission', data.permission) + } + + function test_notification() { + grantPermission = true + + view.url = resolverUrl('notification.html') + view.waitForLoadSucceeded() + + view.runJavaScript('requestPermission()') + spyRequest.wait() + verify(permissionRequested) + + let title = 'Title', message = 'Message', notification = null + view.profile.userNotification.connect(function (n) { notification = n }) + + view.runJavaScript('sendNotification("' + title + '", "' + message + '")') + tryVerify(function () { return notification !== null }) + compare(notification.title, title) + compare(notification.message, message) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_profile.qml b/tests/auto/quick/qmltests/data/tst_profile.qml new file mode 100644 index 000000000..ee7fa4e99 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_profile.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.9 + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + + + WebEngineProfile { + id: profile1 + } + WebEngineProfile { + id: profile2 + } + property bool profile1UsedForGlobalCertificateVerification: profile1.useForGlobalCertificateVerification + + TestCase { + name: "WebEngineProfile" + + function test_useForGlobalCertificateVerification() { + verify(!profile1.useForGlobalCertificateVerification); + verify(!profile2.useForGlobalCertificateVerification); + verify(!webEngineView.profile1UsedForGlobalCertificateVerification); + + profile1.useForGlobalCertificateVerification = true; + verify(profile1.useForGlobalCertificateVerification); + verify(!profile2.useForGlobalCertificateVerification); + verify(webEngineView.profile1UsedForGlobalCertificateVerification); + + profile2.useForGlobalCertificateVerification = true; + verify(!webEngineView.profile1UsedForGlobalCertificateVerification); + verify(!profile1.useForGlobalCertificateVerification); + verify(profile2.useForGlobalCertificateVerification); + } + } +} diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index a2b05e091..00e884e11 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -67,6 +67,8 @@ OTHER_FILES += \ $$PWD/data/tst_navigationHistory.qml \ $$PWD/data/tst_navigationRequested.qml \ $$PWD/data/tst_newViewRequest.qml \ + $$PWD/data/tst_notification.qml \ + $$PWD/data/tst_profile.qml \ $$PWD/data/tst_properties.qml \ $$PWD/data/tst_runJavaScript.qml \ $$PWD/data/tst_scrollPosition.qml \ diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST index 76cb18c1e..166a6894e 100644 --- a/tests/auto/quick/qquickwebengineview/BLACKLIST +++ b/tests/auto/quick/qquickwebengineview/BLACKLIST @@ -9,8 +9,9 @@ windows [basicRenderingSanity] * + [javascriptClipboard:default] opensuse-leap + [javascriptClipboard:canPaste] opensuse-leap - diff --git a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro b/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro index c253bc2a6..38c130aa3 100644 --- a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro +++ b/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro @@ -1,6 +1,6 @@ include(../tests.pri) exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT_PRIVATE += webengine-private gui-private webenginecore-private +QT_PRIVATE += core_private gui-private webengine-private webenginecore-private HEADERS += ../shared/util.h diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 007acb8b0..9817e7d6c 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -31,6 +31,7 @@ #include <QScopedPointer> #include <QtCore/qelapsedtimer.h> +#include <QtCore/qregularexpression.h> #include <QtGui/qclipboard.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qwindowsysteminterface.h> @@ -40,6 +41,7 @@ #include <QtGui/private/qinputmethod_p.h> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebenginesettings_p.h> +#include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qpa/qplatforminputcontext.h> #include <functional> @@ -999,18 +1001,20 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); + errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); QLocale::setDefault(QLocale("en")); QScopedPointer<QQuickWebEngineView> viewEN(newWebEngineView()); viewEN->setUrl(url); QVERIFY(waitForLoadFailed(viewEN.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached")); + errorLines = evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached non.existent\xE2\x80\x99s server IP address could not be found.")); // Reset error page viewDE->setUrl(QUrl("about:blank")); @@ -1020,9 +1024,10 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); + errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); } void tst_QQuickWebEngineView::userScripts() diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 50a6a8587..46dd57f76 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -1,5 +1,5 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) # workaround for QTBUG-68093 +QT_FOR_CONFIG += webengine-private TEMPLATE = subdirs diff --git a/tests/auto/quick/tests.pri b/tests/auto/quick/tests.pri index f7104ad9c..1bf69da43 100644 --- a/tests/auto/quick/tests.pri +++ b/tests/auto/quick/tests.pri @@ -1,5 +1,5 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) # workaround for QTBUG-68093 +QT_FOR_CONFIG += webengine-private TEMPLATE = app diff --git a/tests/auto/shared/data/notification.html b/tests/auto/shared/data/notification.html new file mode 100644 index 000000000..cadcbd942 --- /dev/null +++ b/tests/auto/shared/data/notification.html @@ -0,0 +1,70 @@ +<!doctype html> +<html> +<head> +<title>Desktop Notifications Demo</title> +<script> + function resetPermission() { document.Notification = 'default' } + + function getPermission() { return document.Notification } + + function sendNotification(title, body) { + let notification = new Notification(title, { body: body }) + notification.onclick = function() { console.info('onclick') } + notification.onclose = function() { console.info('onclose') } + notification.onerror = function(error) { console.info('onerror: ' + error) } + notification.onshow = function() { console.info('onshow') } + } + + function makeNotification() { + let title = document.getElementById("title").value + let body = document.getElementById("body").value + console.log('making notification:', title) + sendNotification(title, body) + } + + function requestPermission(callback) { + Notification.requestPermission().then(function (permission) { + document.Notification = permission + if (callback) + callback(permission) + }) + } + + function displayNotification() { + console.info('notifications are ' + document.Notification) + + let state = document.getElementById('state') + + if (document.Notification === 'denied') { + state.innerHTML = 'Notifications disabled' + } else if (document.Notification === 'granted') { + makeNotification() + state.innerHTML = 'notification created' + } else { + state.innerHTML = 'requesting permission...' + requestPermission(function (permission) { + console.info('notifications request: ' + permission) + if (permission === 'granted') { + makeNotification() + state.innerHTML = 'permission granted, notification created' + } else if (permission === 'denied') + state.innerHTML = 'Notifications are disabled' + }) + } + } + + document.addEventListener("DOMContentLoaded", function() { + document.Notification = Notification.permission + }) +</script> +</head> +<body> + <form name="NotificationForm" id="notificationForm"> + Title: <input type="text" id="title" placeholder="Notification title" value='sample title'><br> + Body: <input type="text" id="body" placeholder="Notification body" value='default body'><br> + <input type="button" value="Display Notification" onclick="displayNotification()"><br> + <input type="button" value="Reset Permission" onclick="resetPermission()"> + </form> + <div id='state'></div> +</body> +</html> diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp index 59cbdae13..61d16bc8a 100644 --- a/tests/auto/widgets/origins/tst_origins.cpp +++ b/tests/auto/widgets/origins/tst_origins.cpp @@ -301,7 +301,7 @@ void tst_Origins::jsUrlOrigin() QCOMPARE(eval(QSL("new URL(\"qrc:/crysis.css\").origin")), QVariant(QSL("qrc://"))); QCOMPARE(eval(QSL("new URL(\"qrc://foo.com/crysis.css\").origin")), QVariant(QSL("qrc://"))); - // Same with unregistered schemes. + // Unregistered schemes behaves like opaque origins. QCOMPARE(eval(QSL("new URL(\"tst:/banana\").origin")), QVariant(QSL("tst://"))); QCOMPARE(eval(QSL("new URL(\"tst://foo.com/banana\").origin")), QVariant(QSL("tst://"))); @@ -564,8 +564,6 @@ private: // Try opening a WebSocket from pages loaded over various URL schemes. void tst_Origins::webSocket() { - const int kAbnormalClosure = 1006; - EchoServer echoServer; QWebChannel channel; channel.registerObject(QSL("echoServer"), &echoServer); @@ -578,9 +576,9 @@ void tst_Origins::webSocket() QVERIFY(load(QSL("qrc:/resources/websocket.html"))); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); - // Only registered schemes can open WebSockets. + // Unregistered schemes can also open WebSockets (since Chromium 71) QVERIFY(load(QSL("tst:/resources/websocket.html"))); - QTRY_COMPARE(eval(QSL("result")), QVariant(kAbnormalClosure)); + QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); // Even an insecure registered scheme can open WebSockets. QVERIFY(load(QSL("PathSyntax:/resources/websocket.html"))); @@ -599,11 +597,10 @@ void tst_Origins::dedicatedWorker() QTRY_VERIFY(eval(QSL("done")).toBool()); QCOMPARE(eval(QSL("result")), QVariant(42)); - // Unregistered schemes cannot create Workers. + // Unregistered schemes can also create Workers (since Chromium 71) QVERIFY(load(QSL("tst:/resources/dedicatedWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); - QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Access to dedicated workers is denied to origin 'tst://'"))); + QCOMPARE(eval(QSL("result")), QVariant(42)); // Even an insecure registered scheme can create Workers. QVERIFY(load(QSL("PathSyntax:/resources/dedicatedWorker.html"))); @@ -724,15 +721,9 @@ void tst_Origins::createObjectURL() QVERIFY(load(QSL("qrc:/resources/createObjectURL.html"))); QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:qrc:"))); - // Illegal for unregistered schemes (renderer gets terminated). - qRegisterMetaType<QWebEnginePage::RenderProcessTerminationStatus>("RenderProcessTerminationStatus"); - QSignalSpy loadFinishedSpy(m_page, &QWebEnginePage::loadFinished); - QSignalSpy renderProcessTerminatedSpy(m_page, &QWebEnginePage::renderProcessTerminated); - m_page->load(QSL("tst:/resources/createObjectURL.html")); - QVERIFY(!renderProcessTerminatedSpy.empty() || renderProcessTerminatedSpy.wait(20000)); - QVERIFY(renderProcessTerminatedSpy.front().value(0).value<QWebEnginePage::RenderProcessTerminationStatus>() - != QWebEnginePage::NormalTerminationStatus); - QVERIFY(loadFinishedSpy.empty()); + // Also legal for unregistered schemes (since Chromium 71) + QVERIFY(load(QSL("tst:/resources/createObjectURL.html"))); + QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:tst:"))); } QTEST_MAIN(tst_Origins) diff --git a/tests/auto/widgets/proxy/proxy.pro b/tests/auto/widgets/proxy/proxy.pro new file mode 100644 index 000000000..802dfad05 --- /dev/null +++ b/tests/auto/widgets/proxy/proxy.pro @@ -0,0 +1,9 @@ +include(../tests.pri) +QT += core-private webengine webengine-private + +HEADERS += \ + proxy_server.h + +SOURCES += \ + proxy_server.cpp + diff --git a/tests/auto/widgets/proxy/proxy_server.cpp b/tests/auto/widgets/proxy/proxy_server.cpp new file mode 100644 index 000000000..55f014914 --- /dev/null +++ b/tests/auto/widgets/proxy/proxy_server.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "proxy_server.h" +#include <QDataStream> +#include <QTcpSocket> +#include <QDebug> + +ProxyServer::ProxyServer(QObject *parent) : QObject(parent) +{ + connect(&m_server, &QTcpServer::newConnection, this, &ProxyServer::handleNewConnection); +} + +void ProxyServer::setCredentials(const QByteArray &user, const QByteArray password) +{ + m_auth.append(user); + m_auth.append(QChar(':')); + m_auth.append(password); + m_auth = m_auth.toBase64(); +} + +bool ProxyServer::isListening() +{ + return m_server.isListening(); +} + +void ProxyServer::run() +{ + if (!m_server.listen(QHostAddress::LocalHost, 5555)) + qFatal("Could not start the test server"); +} + +void ProxyServer::handleNewConnection() +{ + // do one connection at the time + Q_ASSERT(m_data.isEmpty()); + QTcpSocket *socket = m_server.nextPendingConnection(); + Q_ASSERT(socket); + connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); + connect(socket, &QAbstractSocket::readyRead, this, &ProxyServer::handleReadReady); +} + +void ProxyServer::handleReadReady() +{ + QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); + Q_ASSERT(socket); + + m_data.append(socket->readAll()); + + if (!m_data.endsWith("\r\n\r\n")) + return; + + if (!m_data.contains(QByteArrayLiteral("Proxy-Authorization: Basic"))) { + socket->write("HTTP/1.1 407 Proxy Authentication Required\nProxy-Authenticate: " + "Basic realm=\"Proxy requires authentication\"\r\n" + "content-length: 0\r\n" + "\r\n"); + return; + } + + if (m_data.contains(m_auth)) { + emit success(); + } + m_data.clear(); +} diff --git a/tests/auto/widgets/proxy/proxy_server.h b/tests/auto/widgets/proxy/proxy_server.h new file mode 100644 index 000000000..cb7c30600 --- /dev/null +++ b/tests/auto/widgets/proxy/proxy_server.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROXY_SERVER_H +#define PROXY_SERVER_H + +#include <QObject> +#include <QTcpServer> + +class ProxyServer : public QObject +{ + Q_OBJECT + +public: + explicit ProxyServer(QObject *parent = nullptr); + void setCredentials(const QByteArray &user, const QByteArray password); + bool isListening(); + +public slots: + void run(); + +private slots: + void handleNewConnection(); + void handleReadReady(); + +signals: + void success(); +private: + QByteArray m_data; + QTcpServer m_server; + QByteArray m_auth; +}; + +#endif // PROXY_SERVER_H diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp new file mode 100644 index 000000000..5f5dec016 --- /dev/null +++ b/tests/auto/widgets/proxy/tst_proxy.cpp @@ -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:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "proxy_server.h" +#include <QTest> +#include <QSignalSpy> +#include <QNetworkProxy> +#include <QWebEnginePage> +#include <QWebEngineView> + + +class tst_Proxy : public QObject { + Q_OBJECT +public: + tst_Proxy(){} + +private slots: + void proxyAuthentication(); +}; + +void tst_Proxy::proxyAuthentication() +{ + QByteArray user(QByteArrayLiteral("test")); + QByteArray password(QByteArrayLiteral("pass")); + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("localhost"); + proxy.setPort(5555); + proxy.setUser(user); + proxy.setPassword(password); + QNetworkProxy::setApplicationProxy(proxy); + ProxyServer server; + server.setCredentials(user,password); + server.run(); + QTRY_VERIFY2(server.isListening(), "Could not setup authentication server"); + QWebEnginePage page; + QSignalSpy successSpy(&server, &ProxyServer::success); + page.load(QUrl("http://www.qt.io")); + QTRY_VERIFY2(successSpy.count() > 0, "Could not get authentication token"); +} + +#include "tst_proxy.moc" +QTEST_MAIN(tst_Proxy) + diff --git a/tests/auto/widgets/proxypac/proxypac.pro b/tests/auto/widgets/proxypac/proxypac.pro index 00ae90977..1c2958d3a 100644 --- a/tests/auto/widgets/proxypac/proxypac.pro +++ b/tests/auto/widgets/proxypac/proxypac.pro @@ -3,26 +3,8 @@ QT += webengine HEADERS += proxyserver.h SOURCES += proxyserver.cpp -# QTBUG-71229 -xgd_desktop.name=XDG_CURRENT_DESKTOP -xgd_desktop.value=KDE -QT_TOOL_ENV += xgd_desktop +proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS +proxy_pac.value = --proxy-pac-url="file://$$PWD/proxy.pac" -kde_home.name=KDEHOME -kde_home.value=$$OUT_PWD -QT_TOOL_ENV += kde_home - -PROXY_CONFIG= \ - "[Proxy Settings]" \ - "Proxy Config Script=$$PWD/proxy.pac" \ - "ProxyType=2" - -mkpath($$OUT_PWD/share/config) -KDE_FILE = $$OUT_PWD/share/config/kioslaverc - -!build_pass { - write_file($$KDE_FILE, PROXY_CONFIG) -} - -QMAKE_DISTCLEAN += $$KDE_FILE +QT_TOOL_ENV += proxy_pac diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index b30fc7258..ee1e6ee04 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -31,6 +31,7 @@ #include <QStandardPaths> #include <QTemporaryDir> #include <QTest> +#include <QRegularExpression> #include <QWebEngineDownloadItem> #include <QWebEnginePage> #include <QWebEngineProfile> @@ -70,6 +71,12 @@ private Q_SLOTS: void downloadFileNot2(); void downloadDeleted(); void downloadDeletedByProfile(); + void downloadUniqueFilename_data(); + void downloadUniqueFilename(); + void downloadUniqueFileNameWithTimeStamp(); + void downloadToDefaultLocation(); + void downloadToNonExistentDir(); + void downloadToReadOnlyDir(); private: void saveLink(QPoint linkPos); @@ -135,6 +142,8 @@ void tst_QWebEngineDownloadItem::cleanup() QTRY_COMPARE(m_requestedDownloads.count(), 0); QCOMPARE(m_finishedDownloads.count(), 0); QVERIFY(m_server->stop()); + // Set download path to default. + m_profile->setDownloadPath(""); } void tst_QWebEngineDownloadItem::cleanupTestCase() @@ -844,5 +853,264 @@ void tst_QWebEngineDownloadItem::downloadDeletedByProfile() QTRY_COMPARE(downloadItem.isNull(), true); } +void tst_QWebEngineDownloadItem::downloadUniqueFilename_data() +{ + QTest::addColumn<QString>("baseName"); + QTest::addColumn<QString>("extension"); + + QTest::newRow("txt") << QString("test(1.test)") << QString("txt"); + QTest::newRow("tar.gz") << QString("test(1.test)") << QString("tar.gz"); +} + +void tst_QWebEngineDownloadItem::downloadUniqueFilename() +{ + QFETCH(QString, baseName); + QFETCH(QString, extension); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->type(), QWebEngineDownloadItem::Attachment); + QCOMPARE(item->isSavePageDownload(), false); + downloadedFilePath = item->path(); + downloadFinished = true; + }); + }); + + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + "." + extension); + + for (int i = 1; i <= 2; ++i) { + downloadFinished = false; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension); + } +} + +void tst_QWebEngineDownloadItem::downloadUniqueFileNameWithTimeStamp() +{ + // Set up HTTP server + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->page(), m_page); + downloadFinished = true; + downloadedFilePath = item->path(); + }); + }); + + // Create the first empty file without uniquifier. + { + QFile file(m_profile->downloadPath() + "/" + fileName); + file.open(QIODevice::ReadWrite); + } + + // Create 99 empty files with uniquifier. + for (int i = 1; i < 100; i++) { + QFile file(m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension); + file.open(QIODevice::ReadWrite); + } + + // Create 100th (kMaxUniqueFiles) empty file with uniquifier. + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (100)." + extension); + + // Check if the downloaded files are suffixed with timestamp after the 100th download. + for (int i = 101; i < 103; i++) { + downloadFinished = false; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QRegularExpression fileNameCheck("^.*" + QRegularExpression::escape(baseName) + " - (.*)[.]" + QRegularExpression::escape(extension) + "$"); + QRegularExpressionMatch match = fileNameCheck.match(downloadedFilePath); + QVERIFY(match.hasMatch()); + // ISO 8601 Date and time in UTC + QRegExp timeStamp("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])([.][0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$"); + QVERIFY(timeStamp.exactMatch(match.captured(1))); + } +} + +void tst_QWebEngineDownloadItem::downloadToDefaultLocation() +{ + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + + m_profile->setDownloadPath(""); + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + + m_profile->setDownloadPath(tmpDir.path()); + QCOMPARE(m_profile->downloadPath(), tmpDir.path()); + + m_profile->setDownloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); +} + +void tst_QWebEngineDownloadItem::downloadToNonExistentDir() +{ + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->page(), m_page); + downloadFinished = true; + downloadedFilePath = item->path(); + }); + }); + + // Set a non-existent directory for the default download location. + QString nonExistentDownloadPath(m_profile->downloadPath() + "/non_existent_dir"); + m_profile->setDownloadPath(nonExistentDownloadPath); + QCOMPARE(m_profile->downloadPath(), nonExistentDownloadPath); + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, nonExistentDownloadPath + "/" + fileName); +} + +void tst_QWebEngineDownloadItem::downloadToReadOnlyDir() +{ + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadAccepted = false; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + QPointer<QWebEngineDownloadItem> downloadItem; + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + downloadItem = item; + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + downloadFinished = true; + }); + downloadAccepted = true; + }); + + // Change permission for directory. + QFile(m_profile->downloadPath()).setPermissions(QFileDevice::ReadOwner); + QVERIFY(QFile(m_profile->downloadPath()).exists()); + + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadAccepted); + + QVERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadInterrupted); + QCOMPARE(downloadItem->isFinished(), false); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::FileAccessDenied); + QVERIFY(!QFile(downloadedFilePath).exists()); + + // Clear m_requestedDownloads explicitly because download is accepted but never finished. + m_requestedDownloads.clear(); + QVERIFY(!downloadFinished); + QFile(m_profile->downloadPath()).setPermissions(QFileDevice::WriteOwner); +} + QTEST_MAIN(tst_QWebEngineDownloadItem) #include "tst_qwebenginedownloaditem.moc" diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index fdff57254..f4d0ad1a0 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -34,7 +34,6 @@ #include <QOpenGLWidget> #include <QPaintEngine> #include <QPushButton> -#include <QRegExp> #include <QScreen> #include <QStateMachine> #include <QtGui/QClipboard> @@ -50,6 +49,7 @@ #include <qwebenginedownloaditem.h> #include <qwebenginefullscreenrequest.h> #include <qwebenginehistory.h> +#include <qwebenginenotification.h> #include <qwebenginepage.h> #include <qwebengineprofile.h> #include <qwebenginequotarequest.h> @@ -90,11 +90,8 @@ public Q_SLOTS: private Q_SLOTS: void initTestCase(); void cleanupTestCase(); - void thirdPartyCookiePolicy(); void comboBoxPopupPositionAfterMove(); void comboBoxPopupPositionAfterChildMove(); - void contextMenuCopy(); - void contextMenuPopulatedOnce(); void acceptNavigationRequest(); void acceptNavigationRequestNavigationType(); void geolocationRequestJS_data(); @@ -103,26 +100,13 @@ private Q_SLOTS: void actionStates(); void pasteImage(); void popupFormSubmission(); - void userStyleSheet(); - void userStyleSheetFromLocalFileUrl(); - void userStyleSheetFromQrcUrl(); - void modified(); - void contextMenuCrash(); - void updatePositionDependentActionsCrash(); void callbackSpyDeleted(); void multipleProfilesAndLocalStorage(); - void cursorMovements(); void textSelection(); - void textEditing(); void backActionUpdate(); - void testOptionalJSObjects(); - void testLocalStorageVisibility(); - void testEnablePersistentStorage(); + void localStorageVisibility(); void consoleOutput(); - void errorPageExtension(); - void errorPageExtensionLoadFinished(); void userAgentNewlineStripping(); - void undoActionHaveCustomText(); void renderWidgetHostViewNotShowTopLevel(); void getUserMediaRequest_data(); void getUserMediaRequest(); @@ -134,30 +118,19 @@ private Q_SLOTS: void crashTests_LazyInitializationOfMainFrame(); - void screenshot_data(); - void screenshot(); - #if defined(ENABLE_WEBGL) && ENABLE_WEBGL void acceleratedWebGLScreenshotWithoutView(); void unacceleratedWebGLScreenshotWithoutView(); #endif void testJSPrompt(); - void testStopScheduledPageRefresh(); void findText(); void findTextResult(); void findTextSuccessiveShouldCallAllCallbacks(); - void supportedContentType(); - // [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT - // https://bugs.webkit.org/show_bug.cgi?id=79040 - // void infiniteLoopJS(); void deleteQWebEngineViewTwice(); - void renderOnRepaintRequestedShouldNotRecurse(); void loadSignalsOrder_data(); void loadSignalsOrder(); void openWindowDefaultSize(); - void cssMediaTypeGlobalSetting(); - void cssMediaTypePageSetting(); #ifdef Q_OS_MAC void macCopyUnicodeToClipboard(); @@ -165,12 +138,12 @@ private Q_SLOTS: void runJavaScript(); void runJavaScriptDisabled(); + void runJavaScriptFromSlot(); void fullScreenRequested(); void quotaRequested(); // Tests from tst_QWebEngineFrame - void horizontalScrollAfterBack(); void symmetricUrl(); void progressSignal(); void urlChange(); @@ -182,8 +155,6 @@ private Q_SLOTS: void setHtmlWithStylesheetResource(); void setHtmlWithBaseURL(); void setHtmlWithJSAlert(); - void inputFieldFocus(); - void hitTestContent(); void baseUrl_data(); void baseUrl(); void scrollPosition(); @@ -191,7 +162,6 @@ private Q_SLOTS: void evaluateWillCauseRepaint(); void setContent_data(); void setContent(); - void setCacheLoadControlAttribute(); void setUrlWithPendingLoads(); void setUrlToEmpty(); void setUrlToInvalid(); @@ -227,6 +197,10 @@ private Q_SLOTS: void triggerActionWithoutMenu(); void dynamicFrame(); + void notificationRequest_data(); + void notificationRequest(); + void sendNotification(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -368,18 +342,6 @@ private: bool m_allowGeolocation; }; -// [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT -// https://bugs.webkit.org/show_bug.cgi?id=79040 -/* -void tst_QWebEnginePage::infiniteLoopJS() -{ - JSTestPage newPage(m_view); - m_view->setPage(&newPage); - m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); - m_view->page()->evaluateJavaScript("var run = true; var a = 1; while (run) { a++; }"); -} -*/ - void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -653,170 +615,6 @@ protected: } }; -void tst_QWebEnginePage::userStyleSheet() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," - + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::userStyleSheetFromLocalFileUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - QUrl styleSheetUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/user.css")); - m_page->settings()->setUserStyleSheetUrl(styleSheetUrl); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::userStyleSheetFromQrcUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - m_page->settings()->setUserStyleSheetUrl(QUrl("qrc:///resources/user.css")); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::modified() -{ -#if !defined(QWEBENGINEPAGE_ISMODIFIED) - QSKIP("QWEBENGINEPAGE_ISMODIFIED"); -#else - m_page->setUrl(QUrl("data:text/html,<body>blub")); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - m_page->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah")); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(!m_page->isModified()); - - m_page->runJavaScript("document.getElementById('foo').focus()"); - evaluateJavaScriptSync(m_page, "document.execCommand('InsertText', true, 'Test');"); - - QVERIFY(m_page->isModified()); - - evaluateJavaScriptSync(m_page, "document.execCommand('Undo', true);"); - - QVERIFY(!m_page->isModified()); - - evaluateJavaScriptSync(m_page, "document.execCommand('Redo', true);"); - - QVERIFY(m_page->isModified()); - - QVERIFY(m_page->history()->canGoBack()); - QVERIFY(!m_page->history()->canGoForward()); - QCOMPARE(m_page->history()->count(), 2); - QVERIFY(m_page->history()->backItem().isValid()); - QVERIFY(!m_page->history()->forwardItem().isValid()); - - m_page->history()->back(); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(!m_page->history()->canGoBack()); - QVERIFY(m_page->history()->canGoForward()); - - QVERIFY(!m_page->isModified()); - - QCOMPARE(m_page->history()->currentItemIndex(), 0); - - m_page->history()->setMaximumItemCount(3); - QCOMPARE(m_page->history()->maximumItemCount(), 3); - - QVariant variant("string test"); - m_page->history()->currentItem().setUserData(variant); - QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); - - m_page->setUrl(QUrl("data:text/html,<body>This is second page")); - m_page->setUrl(QUrl("data:text/html,<body>This is third page")); - QCOMPARE(m_page->history()->count(), 2); - m_page->setUrl(QUrl("data:text/html,<body>This is fourth page")); - QCOMPARE(m_page->history()->count(), 2); - m_page->setUrl(QUrl("data:text/html,<body>This is fifth page")); - QSignalSpy spy(m_page, &QWebEnginePage::saveFrameStateRequested); - QVERIFY(spy.wait()); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=51331 -void tst_QWebEnginePage::updatePositionDependentActionsCrash() -{ -#if !defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS) - QSKIP("QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS"); -#else - QWebEngineView view; - view.setHtml("<p>test"); - QPoint pos(0, 0); - view.page()->updatePositionDependentActions(pos); - QMenu* contextMenu = 0; - const QList<QObject *> children = view.children(); - for (QObject *child : children) { - contextMenu = qobject_cast<QMenu*>(child); - if (contextMenu) - break; - } - QVERIFY(!contextMenu); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=20357 -void tst_QWebEnginePage::contextMenuCrash() -{ -#if !defined(QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT) - QSKIP("QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT"); -#else - QWebEngineView view; - view.setHtml("<p>test"); - QPoint pos(0, 0); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - QMenu* contextMenu = 0; - const QList<QObject *> children = view.children(); - for (QObject *child : children) { - contextMenu = qobject_cast<QMenu*>(child); - if (contextMenu) - break; - } - QVERIFY(contextMenu); - delete contextMenu; -#endif -} - void tst_QWebEnginePage::multipleProfilesAndLocalStorage() { QDir dir(tmpDirPath()); @@ -889,204 +687,6 @@ public: } }; -void tst_QWebEnginePage::cursorMovements() -{ -#if !defined(QWEBENGINEPAGE_SELECTEDTEXT) - QSKIP("QWEBENGINEPAGE_SELECTEDTEXT"); -#else - QScopedPointer<CursorTrackedPage> page(new CursorTrackedPage); - QString content("<html><body><p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>"); - page->setHtml(content); - - // this will select the first paragraph - QString script = "var range = document.createRange(); " \ - "var node = document.getElementById(\"one\"); " \ - "range.selectNode(node); " \ - "getSelection().addRange(range);"; - evaluateJavaScriptSync(page.data(), script); - QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); - - QRegExp regExp(" style=\".*\""); - regExp.setMinimal(true); - QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>")); - - // these actions must exist - QVERIFY(page->action(QWebEnginePage::MoveToNextChar) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousChar) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToNextWord) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousWord) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToNextLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfDocument) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfDocument) != 0); - - // right now they are disabled because contentEditable is false - QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), false); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), true); - - // cursor will be before the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be between 'j' and 'u' in the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 1); - - // cursor will be between 'u' and 'm' in the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 2); - - // cursor will be after the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 5); - - // cursor will be after the word "lazy" - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 19); - - // cursor will be between 'z' and 'y' in "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 18); - - // cursor will be between 'a' and 'z' in "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 17); - - // cursor will be before the word "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 15); - - // cursor will be before the word "quick" - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 4); - - // cursor will be between 'p' and 's' in the word "jumps" - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 4); - - // cursor will be before the word "jumps" - page->triggerAction(QWebEnginePage::MoveToStartOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "dog" - page->triggerAction(QWebEnginePage::MoveToEndOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 23); - - // cursor will be between 'w' and 'n' in "brown" - page->triggerAction(QWebEnginePage::MoveToStartOfLine); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 14); - - // cursor will be after the word "fox" - page->triggerAction(QWebEnginePage::MoveToEndOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 19); - - // cursor will be before the word "The" - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "you!" - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - - // cursor will be before the word "be" - page->triggerAction(QWebEnginePage::MoveToStartOfBlock); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "you!" - page->triggerAction(QWebEnginePage::MoveToEndOfBlock); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - - // try to move before the document start - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // try to move past the document end - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); -#endif -} - void tst_QWebEnginePage::textSelection() { QWebEngineView view; @@ -1101,35 +701,6 @@ void tst_QWebEnginePage::textSelection() // these actions must exist QVERIFY(page->action(QWebEnginePage::SelectAll) != 0); -#if defined(QWEBENGINEPAGE_SELECTACTIONS) - QVERIFY(page->action(QWebEnginePage::SelectNextChar) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousChar) != 0); - QVERIFY(page->action(QWebEnginePage::SelectNextWord) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousWord) != 0); - QVERIFY(page->action(QWebEnginePage::SelectNextLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfDocument) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfDocument) != 0); - - // right now they are disabled because contentEditable is false and - // there isn't an existing selection to modify - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false); -#endif // ..but SelectAll is awalys enabled QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true); @@ -1144,173 +715,10 @@ void tst_QWebEnginePage::textSelection() "getSelection().addRange(range);"; evaluateJavaScriptSync(page, selectScript); QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); -#if defined(QWEBENGINEPAGE_SELECTEDHTML) - QRegExp regExp(" style=\".*\""); - regExp.setMinimal(true); - QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>")); -#endif // Make sure hasSelection returns true, since there is selected text now... QCOMPARE(page->hasSelection(), true); - -#if defined(QWEBENGINEPAGE_SELECTACTIONS) - // here the actions are enabled after a selection has been created - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // cursor will be before the word "The", this makes sure there is a charet - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); -#endif } -void tst_QWebEnginePage::textEditing() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - QScopedPointer<CursorTrackedPage> page(new CursorTrackedPage); - QString content("<html><body><p id=one>The quick brown fox</p>" \ - "<p id=two>jumps over the lazy dog</p>" \ - "<p>May the source<br/>be with you!</p></body></html>"); - page->setHtml(content); - - // these actions must exist - QVERIFY(page->action(QWebEnginePage::Cut) != 0); - QVERIFY(page->action(QWebEnginePage::Copy) != 0); - QVERIFY(page->action(QWebEnginePage::Paste) != 0); - QVERIFY(page->action(QWebEnginePage::DeleteStartOfWord) != 0); - QVERIFY(page->action(QWebEnginePage::DeleteEndOfWord) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionDefault) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionLeftToRight) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionRightToLeft) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleBold) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleItalic) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleUnderline) != 0); - QVERIFY(page->action(QWebEnginePage::InsertParagraphSeparator) != 0); - QVERIFY(page->action(QWebEnginePage::InsertLineSeparator) != 0); - QVERIFY(page->action(QWebEnginePage::PasteAndMatchStyle) != 0); - QVERIFY(page->action(QWebEnginePage::RemoveFormat) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleStrikethrough) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleSubscript) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleSuperscript) != 0); - QVERIFY(page->action(QWebEnginePage::InsertUnorderedList) != 0); - QVERIFY(page->action(QWebEnginePage::InsertOrderedList) != 0); - QVERIFY(page->action(QWebEnginePage::Indent) != 0); - QVERIFY(page->action(QWebEnginePage::Outdent) != 0); - QVERIFY(page->action(QWebEnginePage::AlignCenter) != 0); - QVERIFY(page->action(QWebEnginePage::AlignJustified) != 0); - QVERIFY(page->action(QWebEnginePage::AlignLeft) != 0); - QVERIFY(page->action(QWebEnginePage::AlignRight) != 0); - - // right now they are disabled because contentEditable is false - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), false); - - // Select everything - page->triggerAction(QWebEnginePage::SelectAll); - - // make sure it is enabled since there is a selection - QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), true); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // clear the selection - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // make sure it is disabled since there isn't a selection - QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), false); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), true); - - // make sure these are disabled since there isn't a selection - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); - - // make sure everything is selected - page->triggerAction(QWebEnginePage::SelectAll); - - // this is only true if there is an editable selection - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), true); -#endif -} void tst_QWebEnginePage::backActionUpdate() { @@ -1331,243 +739,39 @@ void tst_QWebEnginePage::backActionUpdate() QVERIFY(action->isEnabled()); } -#if defined(QWEBENGINEPAGE_SETTINGS) -static inline bool testFlag(QWebEnginePage& webPage, QWebEngineSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue) -{ - webPage.settings()->setAttribute(settingAttribute, settingValue); - return evaluateJavaScriptSync(&webPage, QString("(window.%1 != undefined)").arg(jsObjectName)).toBool(); -} -#endif - -void tst_QWebEnginePage::testOptionalJSObjects() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINSETTINGS"); -#else - // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off - // the visibility of the JS object any more. For this reason this test uses two QWebEnginePage instances. - // Part of the test is to make sure that the QWebEnginePage instances do not interfere with each other so turning on - // a feature for one instance will not turn it on for another. - - QWebEnginePage webPage1; - QWebEnginePage webPage2; - - webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - - QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true); - QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true); - - QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", true), true); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), true); -#endif -} - -#if defined(QWEBENGINEPAGE_SETTINGS) -static inline bool checkLocalStorageVisibility(QWebEnginePage& webPage, bool localStorageEnabled) -{ - webPage.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, localStorageEnabled); - return evaluateJavaScriptSync(&webPage, QString("(window.localStorage != undefined)")).toBool(); -} -#endif - -void tst_QWebEnginePage::testLocalStorageVisibility() -{ -#if !defined(QWEBENGINEPAGE_SETTINGS) - QSKIP("QWEBENGINEPAGE_SETTINGS"); -#else - // Local storage's visibility depends on its security origin, which depends on base url. - // Initially, it will test it with base urls that get a globally unique origin, which may not - // be able to use local storage even if the feature is enabled. Then later the same test is - // done but with urls that would get a valid origin, so local storage could be used. - // Before every test case it checks if local storage is not already visible. - - QWebEnginePage webPage; - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("invalid")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("://misparsed.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("about:blank")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("data:text/html,test")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("https://www.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("ftp://files.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///path/to/index.html")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); -#endif -} - -void tst_QWebEnginePage::testEnablePersistentStorage() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - QWebEnginePage webPage; - - // By default all persistent options should be disabled - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), false); - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), false); - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), false); - QVERIFY(webPage.settings()->iconDatabasePath().isEmpty()); - - QWebEngineSettings::enablePersistentStorage(); - - - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), true); - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), true); - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), true); - - QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty()); - QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty()); - QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty()); -#endif -} - - -#if defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) -class ErrorPage : public QWebEnginePage -{ -public: - - ErrorPage(QWidget* parent = 0): QWebEnginePage(parent) - { - } - - virtual bool supportsExtension(Extension extension) const - { - return extension == ErrorPageExtension; - } - - virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output) - { - ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output); - - errorPage->contentType = "text/html"; - errorPage->content = "error"; - return true; - } -}; -#endif - -void tst_QWebEnginePage::errorPageExtension() -{ -#if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) - QSKIP("QWEBENGINEPAGE_ERRORPAGEEXTENSION"); -#else - ErrorPage page; - m_view->setPage(&page); - - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - - m_view->setUrl(QUrl("data:text/html,foo")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - - page.setUrl(QUrl("http://non.existent/url")); - QTRY_COMPARE(spyLoadFinished.count(), 2); - QCOMPARE(toPlainTextSync(&page), QString("error")); - QCOMPARE(page.history()->count(), 2); - QCOMPARE(page.history()->currentItem().url(), QUrl("http://non.existent/url")); - QCOMPARE(page.history()->canGoBack(), true); - QCOMPARE(page.history()->canGoForward(), false); - - page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(page.history()->canGoBack(), false); - QTRY_COMPARE(page.history()->canGoForward(), true); - - page.triggerAction(QWebEnginePage::Forward); - QTRY_COMPARE(page.history()->canGoBack(), true); - QTRY_COMPARE(page.history()->canGoForward(), false); - - page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(page.history()->canGoBack(), false); - QTRY_COMPARE(page.history()->canGoForward(), true); - QTRY_COMPARE(page.history()->currentItem().url(), QUrl("data:text/html,foo")); - - m_view->setPage(0); -#endif -} - -void tst_QWebEnginePage::errorPageExtensionLoadFinished() +void tst_QWebEnginePage::localStorageVisibility() { -#if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) - QSKIP("QWEBENGINEPAGE_ERRORPAGEEXTENSION"); -#else - ErrorPage page; - m_view->setPage(&page); - - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - QSignalSpy spyFrameLoadFinished(m_view->page(), SIGNAL(loadFinished(bool))); - - m_view->setUrl(QUrl("data:text/html,foo")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QTRY_COMPARE(spyFrameLoadFinished.count(), 1); - - const bool loadSucceded = spyLoadFinished.at(0).at(0).toBool(); - QVERIFY(loadSucceded); - const bool frameLoadSucceded = spyFrameLoadFinished.at(0).at(0).toBool(); - QVERIFY(frameLoadSucceded); - - m_view->page()->setUrl(QUrl("http://non.existent/url")); - QTRY_COMPARE(spyLoadFinished.count(), 2); - QTRY_COMPARE(spyFrameLoadFinished.count(), 2); - - const bool nonExistantLoadSucceded = spyLoadFinished.at(1).at(0).toBool(); - QVERIFY(nonExistantLoadSucceded); - const bool nonExistantFrameLoadSucceded = spyFrameLoadFinished.at(1).at(0).toBool(); - QVERIFY(nonExistantFrameLoadSucceded); - - m_view->setPage(0); -#endif + QWebEngineProfile profile; + QWebEnginePage webPage1(&profile); + QWebEnginePage webPage2(&profile); + + webPage1.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + webPage2.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); + + QSignalSpy loadSpy1(&webPage1, &QWebEnginePage::loadFinished); + QSignalSpy loadSpy2(&webPage2, &QWebEnginePage::loadFinished); + webPage1.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); + webPage2.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); + QTRY_COMPARE(loadSpy1.count(), 1); + QTRY_COMPARE(loadSpy2.count(), 1); + + // The attribute determines the visibility of the window.localStorage object. + QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(!evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); + + // Switching the feature off does not actively remove the object from webPage1. + webPage1.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); + webPage2.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); + + // The object disappears only after reloading. + webPage1.triggerAction(QWebEnginePage::Reload); + webPage2.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy1.count(), 2); + QTRY_COMPARE(loadSpy2.count(), 2); + QVERIFY(!evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); } void tst_QWebEnginePage::userAgentNewlineStripping() @@ -1592,70 +796,9 @@ void tst_QWebEnginePage::crashTests_LazyInitializationOfMainFrame() webPage.selectedText(); } { -#if defined(QWEBENGINEPAGE_SELECTEDHTML) - QWebEnginePage webPage; - webPage.selectedHtml(); -#endif - } - { QWebEnginePage webPage; webPage.triggerAction(QWebEnginePage::Back, true); } - { -#if defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS) - QWebEnginePage webPage; - QPoint pos(10,10); - webPage.updatePositionDependentActions(pos); -#endif - } -} - -#if defined(QWEBENGINEPAGE_RENDER) -static void takeScreenshot(QWebEnginePage* page) -{ - page->setViewportSize(page->contentsSize()); - QImage image(page->viewportSize(), QImage::Format_ARGB32); - QPainter painter(&image); - page->render(&painter); - painter.end(); -} -#endif - -void tst_QWebEnginePage::screenshot_data() -{ - QTest::addColumn<QString>("html"); - QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; - QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); - QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>"); -} - -void tst_QWebEnginePage::screenshot() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - if (!QDir(TESTS_SOURCE_DIR).exists()) - W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); - - QDir::setCurrent(TESTS_SOURCE_DIR); - - QFETCH(QString, html); - QWebEnginePage page; - page.settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); - page.setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); - QVERIFY(spyFinished.wait(2000)); - - // take screenshot without a view - takeScreenshot(&page); - - QWebEngineView view; - view.setPage(&page); - - // take screenshot when attached to a view - takeScreenshot(&page); - - QDir::setCurrent(QApplication::applicationDirPath()); -#endif } #if defined(ENABLE_WEBGL) && ENABLE_WEBGL @@ -1754,37 +897,6 @@ void tst_QWebEnginePage::testJSPrompt() QVERIFY(res); } -void tst_QWebEnginePage::testStopScheduledPageRefresh() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - // Without QWebEnginePage::StopScheduledPageRefresh - QWebEnginePage page1; - page1.setNetworkAccessManager(new TestNetworkManager(&page1)); - page1.setHtml("<html><head>" - "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">" - "</head><body><h1>Page redirects immediately...</h1>" - "</body></html>"); - QSignalSpy spyFinished(&page1, &QWebEnginePage::loadFinished); - QVERIFY(spyFinished.wait(); - QTest::qWait(500); - QCOMPARE(page1.url(), QUrl(QLatin1String("qrc:///resources/index.html"))); - - // With QWebEnginePage::StopScheduledPageRefresh - QWebEnginePage page2; - page2.setNetworkAccessManager(new TestNetworkManager(&page2)); - page2.setHtml("<html><head>" - "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">" - "</head><body><h1>Page redirect test with 1 sec timeout...</h1>" - "</body></html>"); - page2.triggerAction(QWebEnginePage::StopScheduledPageRefresh); - QTest::qWait(1500); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118673", Continue); - QCOMPARE(page2.url().toString(), QLatin1String("about:blank")); -#endif -} - void tst_QWebEnginePage::findText() { QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); @@ -1884,95 +996,6 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() QVERIFY(spy5.wasCalled()); } -#if defined(QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES) -static QString getMimeTypeForExtension(const QString &ext) -{ - QMimeType mimeType = QMimeDatabase().mimeTypeForFile(QStringLiteral("filename.") + ext.toLower(), QMimeDatabase::MatchExtension); - if (mimeType.isValid() && !mimeType.isDefault()) - return mimeType.name(); - - return QString(); -} -#endif - -void tst_QWebEnginePage::supportedContentType() -{ -#if !defined(QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES) - QSKIP("QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES"); -#else - QStringList contentTypes; - - // Add supported non image types... - contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/" - << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml" - << "application/rss+xml" << "application/atom+xml" << "application/json"; - -#if ENABLE_MHTML - contentTypes << "application/x-mimearchive"; -#endif - - // Add supported image types... - const QList<QByteArray> supportedImageFormats = QImageWriter::supportedImageFormats(); - for (const QByteArray &imageType : supportedImageFormats) { - const QString mimeType = getMimeTypeForExtension(imageType); - if (!mimeType.isEmpty()) - contentTypes << mimeType; - } - - // Get the mime types supported by webengine... - const QStringList supportedContentTypes = m_page->supportedContentTypes(); - - for (const QString &mimeType : qAsConst(contentTypes)) - QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1()); - - for (const QString &mimeType : qAsConst(contentTypes)) - QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1()); -#endif -} - -void tst_QWebEnginePage::thirdPartyCookiePolicy() -{ -#if !defined(DUMPRENDERTREESUPPORTQT) - QSKIP("DUMPRENDERTREESUPPORTQT"); -#else - QWebEngineSettings::globalSettings()->setThirdPartyCookiePolicy(QWebEngineSettings::AlwaysBlockThirdPartyCookies); - m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); - QVERIFY(m_page->networkAccessManager()->cookieJar()); - - // These are all first-party cookies, so should pass. - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://doc.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://aaa.www.example.com"), QUrl("http://doc.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.com"), QUrl("http://www.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://doc.example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://aaa.www.example.co.uk"), QUrl("http://doc.example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.co.uk"), QUrl("http://www.example.co.uk"))); - - // These are all third-party cookies, so should fail. - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://slashdot.org"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.com"), QUrl("http://anotherexample.com"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://anotherexample.com"), QUrl("http://example.com"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://slashdot.co.uk"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.co.uk"), QUrl("http://anotherexample.co.uk"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://anotherexample.co.uk"), QUrl("http://example.co.uk"))); -#endif -} - static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows) { const auto tlws = QGuiApplication::topLevelWindows(); @@ -2078,66 +1101,6 @@ void tst_QWebEnginePage::macCopyUnicodeToClipboard() } #endif -void tst_QWebEnginePage::contextMenuCopy() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - - view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>"); - - view.page()->triggerAction(QWebEnginePage::SelectAll); - QVERIFY(!view.page()->selectedText().isEmpty()); - - QWebEngineElement link = view.page()->mainFrame()->findFirstElement("a"); - QPoint pos(link.geometry().center()); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - - QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); - QVERIFY(!contextMenus.isEmpty()); - QMenu* contextMenu = contextMenus.first(); - QVERIFY(contextMenu); - - QList<QAction *> list = contextMenu->actions(); - int index = list.indexOf(view.page()->action(QWebEnginePage::Copy)); - QVERIFY(index != -1); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=62139 -void tst_QWebEnginePage::contextMenuPopulatedOnce() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - - view.setHtml("<input type=\"text\">"); - - QWebEngineElement link = view.page()->mainFrame()->findFirstElement("input"); - QPoint pos(link.geometry().center()); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - - QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); - QVERIFY(!contextMenus.isEmpty()); - QMenu* contextMenu = contextMenus.first(); - QVERIFY(contextMenu); - - QList<QAction *> list = contextMenu->actions(); - QStringList entries; - while (!list.isEmpty()) { - QString entry = list.takeFirst()->text(); - QVERIFY(!entries.contains(entry)); - entries << entry; - } -#endif -} - void tst_QWebEnginePage::deleteQWebEngineViewTwice() { for (int i = 0; i < 2; ++i) { @@ -2151,62 +1114,6 @@ void tst_QWebEnginePage::deleteQWebEngineViewTwice() } } -#if defined(QWEBENGINEPAGE_RENDER) -class RepaintRequestedRenderer : public QObject { - Q_OBJECT -public: - RepaintRequestedRenderer(QWebEnginePage* page, QPainter* painter) - : m_page(page) - , m_painter(painter) - , m_recursionCount(0) - { - connect(m_page, SIGNAL(repaintRequested(QRect)), this, SLOT(onRepaintRequested(QRect))); - } - -Q_SIGNALS: - void finished(); - -private Q_SLOTS: - void onRepaintRequested(const QRect& rect) - { - QCOMPARE(m_recursionCount, 0); - - m_recursionCount++; - m_page->render(m_painter, rect); - m_recursionCount--; - - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); - } - -private: - QWebEnginePage* m_page; - QPainter* m_painter; - int m_recursionCount; -}; -#endif - -void tst_QWebEnginePage::renderOnRepaintRequestedShouldNotRecurse() -{ -#if !defined(QWEBENGINEPAGE_RENDER) - QSKIP("QWEBENGINEPAGE_RENDER"); -#else - QSize viewportSize(720, 576); - QWebEnginePage page; - - QImage image(viewportSize, QImage::Format_ARGB32); - QPainter painter(&image); - - page.setPreferredContentsSize(viewportSize); - page.setViewportSize(viewportSize); - RepaintRequestedRenderer r(&page, &painter); - - page.setHtml("zalan loves trunk", QUrl()); - - QSignalSpy spyFinished(&r, &RepaintRequestedRenderer::finished); - QVERIFY(spyFinished.wait()); -#endif -} - class SpyForLoadSignalsOrder : public QStateMachine { Q_OBJECT public: @@ -2265,24 +1172,6 @@ void tst_QWebEnginePage::loadSignalsOrder() QTRY_VERIFY(loadSpy.isFinished()); } -void tst_QWebEnginePage::undoActionHaveCustomText() -{ -#if !defined(QWEBENGINEPAGE_UNDOACTION) - QSKIP("QWEBENGINEPAGE_UNDOACTION"); -#else - m_page->setHtml("<div id=test contenteditable></div>"); - evaluateJavaScriptSync(m_page, "document.getElementById('test').focus()"); - - evaluateJavaScriptSync(m_page, "document.execCommand('insertText', true, 'Test');"); - QString typingActionText = m_page->action(QWebEnginePage::Undo)->text(); - - evaluateJavaScriptSync(m_page, "document.execCommand('indent', true);"); - QString alignActionText = m_page->action(QWebEnginePage::Undo)->text(); - - QVERIFY(typingActionText != alignActionText); -#endif -} - void tst_QWebEnginePage::renderWidgetHostViewNotShowTopLevel() { QWebEnginePage page; @@ -2601,68 +1490,6 @@ void tst_QWebEnginePage::openWindowDefaultSize() QCOMPARE(requestedGeometry.height(), 100); } -void tst_QWebEnginePage::cssMediaTypeGlobalSetting() -{ -#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE) - QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE"); -#else - QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>"); - QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); - - QWebEngineSettings::globalSettings()->setCSSMediaType("tv"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 1); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "tv"); - - QWebEngineSettings::globalSettings()->setCSSMediaType("handheld"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 2); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "handheld"); - - QWebEngineSettings::globalSettings()->setCSSMediaType("screen"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 3); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "screen"); -#endif -} - -void tst_QWebEnginePage::cssMediaTypePageSetting() -{ -#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE) - QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE"); -#else - QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>"); - QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); - - m_view->page()->settings()->setCSSMediaType("tv"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 1); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "tv"); - - m_view->page()->settings()->setCSSMediaType("handheld"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 2); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "handheld"); - - m_view->page()->settings()->setCSSMediaType("screen"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 3); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "screen"); -#endif -} - class JavaScriptCallbackBase { public: @@ -2818,6 +1645,28 @@ void tst_QWebEnginePage::runJavaScriptDisabled() QVariant(2)); } +// Based on https://bugreports.qt.io/browse/QTBUG-73876 +void tst_QWebEnginePage::runJavaScriptFromSlot() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + + QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); + page.setHtml("<html><body>" + " <input type='text' id='input1' value='QtWebEngine' size='50' />" + "</body></html>"); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + + QVariant result(-1); + connect(&page, &QWebEnginePage::selectionChanged, [&]() { + result = evaluateJavaScriptSync(&page, QStringLiteral("2+2")); + }); + evaluateJavaScriptSync(&page, QStringLiteral("const input = document.getElementById('input1');" + "input.focus();" + "input.select();")); + QTRY_COMPARE(result, QVariant(4)); +} + void tst_QWebEnginePage::fullScreenRequested() { JavaScriptCallbackWatcher watcher; @@ -3244,55 +2093,6 @@ void tst_QWebEnginePage::setHtmlWithJSAlert() QCOMPARE(toHtmlSync(&page), html); } -void tst_QWebEnginePage::inputFieldFocus() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - view.setHtml("<html><body><input type=\"text\"></input></body></html>"); - view.resize(400, 100); - view.show(); - QTest::qWaitForWindowExposed(&view); - view.activateWindow(); - view.setFocus(); - QTRY_VERIFY(view.hasFocus()); - - // double the flashing time, should at least blink once already - int delay = qApp->cursorFlashTime() * 2; - - // focus the lineedit and check if it blinks - bool autoSipEnabled = qApp->autoSipEnabled(); - qApp->setAutoSipEnabled(false); - const QWebEngineElement inputElement = view.page()->documentElement().findFirst(QLatin1String("input[type=text]")); - QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); - m_inputFieldsTestView = &view; - view.installEventFilter( this ); - QTest::qWait(delay); - QVERIFY2(m_inputFieldTestPaintCount >= 3, - "The input field should have a blinking caret"); - qApp->setAutoSipEnabled(autoSipEnabled); -#endif -} - -void tst_QWebEnginePage::hitTestContent() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); - - QWebEnginePage page; - page.setHtml(html); - page.setViewportSize(QSize(200, 0)); //no height so link is not visible - const QWebEngineElement linkElement = page.documentElement().findFirst(QLatin1String("a#link")); - QWebEngineHitTestResult result = page.hitTestContent(linkElement.geometry().center()); - QCOMPARE(result.linkText(), QString("link text")); - QWebEngineElement link = result.linkElement(); - QCOMPARE(link.attribute("target"), QString("_foo")); -#endif -} - void tst_QWebEnginePage::baseUrl_data() { QTest::addColumn<QString>("html"); @@ -3373,32 +2173,6 @@ void tst_QWebEnginePage::scrollbarsOff() QVERIFY(evaluateJavaScriptSync(view.page(), "innerWidth == document.documentElement.offsetWidth").toBool()); } -void tst_QWebEnginePage::horizontalScrollAfterBack() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - QWebEngineView view; - QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); - - view.page()->settings()->setMaximumPagesInCache(2); - view.page()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); - view.page()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); - - view.load(QUrl("qrc:/resources/testiframe2.html")); - view.resize(200, 200); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); - - view.load(QUrl("qrc:/resources/testiframe.html")); - QTRY_COMPARE(loadSpy.count(), 2); - - view.page()->triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(loadSpy.count(), 3); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); -#endif -} - class WebView : public QWebEngineView { Q_OBJECT @@ -3493,35 +2267,6 @@ private: QNetworkRequest::CacheLoadControl m_lastCacheLoad; }; -void tst_QWebEnginePage::setCacheLoadControlAttribute() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QWebEnginePage page; - CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); - page.setNetworkAccessManager(manager); - - QNetworkRequest request(QUrl("http://abcdef.abcdef/")); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); -#endif -} - void tst_QWebEnginePage::setUrlWithPendingLoads() { QWebEnginePage page; @@ -4485,6 +3230,96 @@ void tst_QWebEnginePage::dynamicFrame() QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } +struct NotificationPage : ConsolePage { + Q_OBJECT + const QWebEnginePage::PermissionPolicy policy; + +public: + NotificationPage(QWebEnginePage::PermissionPolicy ppolicy) : policy(ppolicy) { + connect(this, &QWebEnginePage::loadFinished, [load = spyLoad.ref()] (bool result) mutable { load(result); }); + + connect(this, &QWebEnginePage::featurePermissionRequested, + [this] (const QUrl &origin, QWebEnginePage::Feature feature) { + if (feature != QWebEnginePage::Notifications) + return; + if (spyRequest.wasCalled()) + QFAIL("request executed twise!"); + setFeaturePermission(origin, feature, policy); + spyRequest.ref()(origin); + }); + + load(QStringLiteral("qrc:///shared/notification.html")); + } + + CallbackSpy<bool> spyLoad; + CallbackSpy<QUrl> spyRequest; + + QString getPermission() { return evaluateJavaScriptSync(this, "getPermission()").toString(); } + void requestPermission() { runJavaScript("requestPermission()"); } + void resetPermission() { runJavaScript("resetPermission()"); } + void sendNotification(const QString &title, const QString &body) { + runJavaScript("sendNotification('" + title + "', '" + body + "')"); + } +}; + +void tst_QWebEnginePage::notificationRequest_data() +{ + QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<QString>("permission"); + QTest::newRow("deny") << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("grant") << QWebEnginePage::PermissionGrantedByUser << "granted"; +} + +void tst_QWebEnginePage::notificationRequest() +{ + QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(QString, permission); + + NotificationPage page(policy); + QVERIFY(page.spyLoad.waitForResult()); + + page.resetPermission(); + QCOMPARE(page.getPermission(), "default"); + + page.requestPermission(); + page.spyRequest.waitForResult(); + QVERIFY(page.spyRequest.wasCalled()); + + QCOMPARE(page.getPermission(), permission); +} + +void tst_QWebEnginePage::sendNotification() +{ + NotificationPage page(QWebEnginePage::PermissionGrantedByUser); + QVERIFY(page.spyLoad.waitForResult()); + + page.resetPermission(); + page.requestPermission(); + auto origin = page.spyRequest.waitForResult(); + QVERIFY(page.spyRequest.wasCalled()); + QCOMPARE(page.getPermission(), "granted"); + + CallbackSpy<QWebEngineNotification> presenter; + page.profile()->setNotificationPresenter([callback = presenter.ref()] (const QWebEngineNotification ¬ification) mutable { callback(notification); }); + + QString title("Title"), message("Message"); + page.sendNotification(title, message); + + auto notification = presenter.waitForResult(); + QVERIFY(presenter.wasCalled()); + QVERIFY(!notification.isNull()); + QCOMPARE(notification.title(), title); + QCOMPARE(notification.message(), message); + QCOMPARE(notification.origin(), origin); + + notification.show(); + QTRY_VERIFY2(page.messages.contains("onshow"), page.messages.join("\n").toLatin1().constData()); + notification.click(); + QTRY_VERIFY2(page.messages.contains("onclick"), page.messages.join("\n").toLatin1().constData()); + notification.close(); + QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData()); +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc index 3bb88cbe1..757e151c1 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc @@ -23,4 +23,7 @@ <file>resources/bar.txt</file> <file>resources/path with spaces.txt</file> </qresource> +<qresource prefix='/shared'> + <file alias='notification.html'>../../shared/data/notification.html</file> +</qresource> </RCC> diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 98b4fa6a8..b0db4e974 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -29,6 +29,7 @@ #include "../util.h" #include <QtCore/qbuffer.h> #include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebengineurlrequestjob.h> #include <QtWebEngineCore/qwebenginecookiestore.h> #include <QtWebEngineCore/qwebengineurlschemehandler.h> @@ -53,6 +54,7 @@ private Q_SLOTS: void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); void urlSchemeHandlerStreaming(); + void urlSchemeHandlerRequestHeaders(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -446,6 +448,65 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming() QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); } +class ExtraHeaderInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + ExtraHeaderInterceptor() { } + + void setExtraHeader(const QByteArray &key, const QByteArray &value) + { + m_extraKey = key; + m_extraValue = value; + } + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + if (info.requestUrl().scheme() == QLatin1String("myscheme")) + info.setHttpHeader(m_extraKey, m_extraValue); + } + + QByteArray m_extraKey; + QByteArray m_extraValue; +}; + +class RequestHeadersUrlSchemeHandler : public ReplyingUrlSchemeHandler +{ +public: + void setExpectedHeader(const QByteArray &key, const QByteArray &value) + { + m_expectedKey = key; + m_expectedValue = value; + } + void requestStarted(QWebEngineUrlRequestJob *job) override + { + const auto requestHeaders = job->requestHeaders(); + QVERIFY(requestHeaders.contains(m_expectedKey)); + QCOMPARE(requestHeaders.value(m_expectedKey), m_expectedValue); + ReplyingUrlSchemeHandler::requestStarted(job); + } + QByteArray m_expectedKey; + QByteArray m_expectedValue; +}; + +void tst_QWebEngineProfile::urlSchemeHandlerRequestHeaders() +{ + RequestHeadersUrlSchemeHandler handler; + ExtraHeaderInterceptor interceptor; + + handler.setExpectedHeader("Hello", "World"); + interceptor.setExtraHeader("Hello", "World"); + + QWebEngineProfile profile; + profile.installUrlSchemeHandler("myscheme", &handler); + profile.setRequestInterceptor(&interceptor); + + QWebEnginePage page(&profile); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl(QStringLiteral("myscheme://whatever"))); + QVERIFY(loadFinishedSpy.wait()); +} + + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); diff --git a/tests/auto/widgets/qwebengineview/resources/image2.png b/tests/auto/widgets/qwebengineview/resources/image2.png Binary files differnew file mode 100644 index 000000000..8d703640c --- /dev/null +++ b/tests/auto/widgets/qwebengineview/resources/image2.png diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 5f05f70ab..17177c7fb 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -47,6 +47,7 @@ #include <QStyle> #include <QtWidgets/qaction.h> #include <QWebEngineProfile> +#include <QtCore/qregularexpression.h> #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ QVERIFY(actual == (expect | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu)); @@ -269,7 +270,7 @@ void tst_QWebEngineView::getWebKitVersion() void tst_QWebEngineView::changePage_data() { QString html = "<html><head><title>%1</title>" - "<link rel='icon' href='file://" TESTS_SOURCE_DIR "/resources/image2.png'></head></html>"; + "<link rel='icon' href='qrc:///resources/image2.png'></head></html>"; QUrl urlFrom("data:text/html," + html.arg("TitleFrom")); QUrl urlTo("data:text/html," + html.arg("TitleTo")); QUrl nullPage("data:text/html,<html/>"); @@ -1182,7 +1183,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); - errorLines = toPlainTextSync(viewDE.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); QLocale::setDefault(QLocale("en")); @@ -1192,7 +1193,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewEN.page()).isEmpty()); - errorLines = toPlainTextSync(viewEN.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewEN.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached")); // Reset error page @@ -1205,7 +1206,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); - errorLines = toPlainTextSync(viewDE.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); } @@ -2636,7 +2637,7 @@ void tst_QWebEngineView::imeJSInputEvents() view.show(); auto logLines = [&view]() -> QStringList { - return evaluateJavaScriptSync(view.page(), "log.textContent").toString().split("\n").filter(QRegExp(".+")); + return evaluateJavaScriptSync(view.page(), "log.textContent").toString().split("\n").filter(QRegularExpression(".+")); }; QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc index 53b11bca8..a09be0399 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc @@ -5,5 +5,6 @@ <file>resources/input_types.html</file> <file>resources/scrolltest_page.html</file> <file>resources/keyboardEvents.html</file> + <file>resources/image2.png</file> </qresource> </RCC> diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index eec8bb389..3f0e6dc3b 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -9,6 +9,8 @@ SUBDIRS += \ faviconmanager \ loadsignals \ origins \ + proxy \ + proxypac \ schemes \ shutdown \ qwebenginedownloaditem \ @@ -27,9 +29,6 @@ qtConfig(webengine-printing-and-pdf) { SUBDIRS += printing } -# QTBUG-71229 -linux:!boot2qt: SUBDIRS += proxypac - qtConfig(webengine-spellchecker):!cross_compile { !qtConfig(webengine-native-spellchecker) { SUBDIRS += spellchecking @@ -43,4 +42,4 @@ boot2qt: SUBDIRS -= accessibility defaultsurfaceformat devtools \ faviconmanager qwebenginepage qwebenginehistory \ qwebengineprofile qwebenginescript \ qwebengineview qwebenginedownloaditem qwebenginesettings \ - schemes origins loadsignals + schemes origins loadsignals proxy proxypac diff --git a/tests/manual/widgets/inputmethods/testview.cpp b/tests/manual/widgets/inputmethods/testview.cpp index d41734c2d..14e355caf 100644 --- a/tests/manual/widgets/inputmethods/testview.cpp +++ b/tests/manual/widgets/inputmethods/testview.cpp @@ -31,7 +31,7 @@ #include <QDebug> #include <QFile> #include <QPushButton> -#include <QRegExp> +#include <QRegularExpression> #include <QStandardItemModel> #include <QTableView> #include <QTest> @@ -77,15 +77,16 @@ void TestView::loadTestData(const QString &testDataPath) QTextStream testDataStream(&testDataFile); while (!testDataStream.atEnd()) { QString line = testDataStream.readLine(); - QRegExp data("^\"(.*)\"\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"\\s*$"); - if (!data.exactMatch(line)) + QRegularExpression data(QRegularExpression::anchoredPattern("^\"(.*)\"\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"\\s*$")); + QRegularExpressionMatch match = data.match(line); + if (!match.hasMatch()) continue; QStandardItemModel *model = qobject_cast<QStandardItemModel *>(m_tableView->model()); QList<QStandardItem *> row; - for (int i = 1; i <= data.captureCount(); ++i) - row.append(new QStandardItem(data.cap(i))); + for (int i = 1; i <= match.lastCapturedIndex(); ++i) + row.append(new QStandardItem(match.captured(i))); model->appendRow(row); } diff --git a/tests/manual/widgets/webgl/main.cpp b/tests/manual/widgets/webgl/main.cpp new file mode 100644 index 000000000..364eda8b9 --- /dev/null +++ b/tests/manual/widgets/webgl/main.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QDebug> +#include <QtCore/QLoggingCategory> +#include <QtCore/QOperatingSystemVersion> +#include <QtGui/QOpenGLContext> +#include <QtGui/QSurfaceFormat> +#include <QtWidgets/QApplication> +#include <QtWidgets/QDesktopWidget> +#include <QtWidgets/QGroupBox> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QMainWindow> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QVBoxLayout> +#include <QtWebEngineWidgets/QWebEngineView> + +// Manual test for checking if WebGL (1 or 2) works. +// Set environment variable QTWEBENGINE_GL_TYPE to one of the following to try and switch +// the underlying GL implementation (mostly Windows): "desktop", "gles", "gles3", "software". + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + QSize sizeHint() const; + +private: + QWebEngineView *view = nullptr; +}; + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + QWidget *centralWidget = new QWidget; + + QGroupBox *horizontalGroupBox = new QGroupBox; + QHBoxLayout *buttonLayout = new QHBoxLayout; + QPushButton *exButton1 = new QPushButton(QStringLiteral("Aquarium (WebGL 1)")); + QPushButton *exButton2 = new QPushButton(QStringLiteral("Lots of objects (WebGL 1)")); + QPushButton *exButton3 = new QPushButton(QStringLiteral("Instanced triangles (WebGL 2)")); + + buttonLayout->addWidget(exButton1); + buttonLayout->addWidget(exButton2); + buttonLayout->addWidget(exButton3); + horizontalGroupBox->setLayout(buttonLayout); + + const QUrl exUrl1 = + QUrl(QStringLiteral( + "http://webglsamples.org/aquarium/aquarium.html")); + const QUrl exUrl2 = + QUrl(QStringLiteral( + "http://webglsamples.org/lots-o-objects/lots-o-objects-draw-elements.html")); + const QUrl exUrl3 = + QUrl(QStringLiteral( + "http://webglsamples.org/WebGL2Samples/#transform_feedback_instanced")); + + view = new QWebEngineView; + connect(exButton1, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl1); + }); + connect(exButton2, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl2); + }); + connect(exButton3, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl3); + }); + + QVBoxLayout *centralLayout = new QVBoxLayout; + centralLayout->addWidget(horizontalGroupBox); + centralLayout->addWidget(view, 1); + + centralWidget->setLayout(centralLayout); + setCentralWidget(centralWidget); + + view->setUrl(QUrl(QStringLiteral("http://webglsamples.org/aquarium/aquarium.html"))); + setWindowTitle(tr("WebGL 1 and 2 examples")); +} + +QSize MainWindow::sizeHint() const +{ + const QRect desktopRect = QApplication::desktop()->screenGeometry(); + const QSize size = desktopRect.size() * qreal(0.9); + return size; +} + +bool isWindows() +{ + return QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows; +} + +// Easy snippets to copy to command line for testing for UNIX only. +// QTWEBENGINE_GL_TYPE=desktop ./webgl +// QTWEBENGINE_GL_TYPE=gles ./webgl +// QTWEBENGINE_GL_TYPE=gles3 ./webgl +// QTWEBENGINE_GL_TYPE=software ./webgl +int main(int argc, char *argv[]) +{ + // Not all options are relevant for all platforms. + const QString desktopGL = QStringLiteral("desktop"); + const QString angle = QStringLiteral("angle"); // Same as gles really, just an alias. + const QString gles = QStringLiteral("gles"); // ANGLE on Windows. + const QString gles3 = QStringLiteral("gles3"); // ANGLE on Windows. + const QString softwareGL = QStringLiteral("software"); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + QString glType = qEnvironmentVariable("QTWEBENGINE_GL_TYPE"); + if (glType.isEmpty()) { + if (isWindows()) + glType = gles3; + else + glType = desktopGL; + } + + if (glType == desktopGL) { + QApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + qInfo() << QStringLiteral("Trying to use Desktop OpenGL.\n"); + } else if (glType == gles || glType == gles3 || glType == angle) { + QApplication::setAttribute(Qt::AA_UseOpenGLES); + if (glType == gles || glType == angle) + qInfo() << QStringLiteral("Trying to use OpenGL ES 2.\n"); + if (glType == gles3) + qInfo() << QStringLiteral("Trying to use OpenGL ES 3.\n"); + } else if (glType == softwareGL) { + QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + qInfo() << QStringLiteral("Trying to use software OpenGL.\n"); + } + + if (glType == gles3) { + // Set OpenGL ES version 3. + QSurfaceFormat format; + format.setSamples(4); + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(3, 0); + QSurfaceFormat::setDefaultFormat(format); + } + + QApplication a(argc, argv); + + QLoggingCategory::setFilterRules(QStringLiteral("qt.scenegraph.general=true")); + QOpenGLContext *globalSharedContext = QOpenGLContext::globalShareContext(); + qInfo() << "Global OpenGL context format: " << globalSharedContext->format() << "\n"; + + MainWindow w; + + // Move middle-ish. + const QRect desktopRect = QApplication::desktop()->screenGeometry(); + const QSize pos = desktopRect.size() * qreal(0.1); + w.move(pos.width(), pos.height()); + + w.show(); + + return a.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/webgl/webgl.pro b/tests/manual/widgets/webgl/webgl.pro new file mode 100644 index 000000000..9141d0221 --- /dev/null +++ b/tests/manual/widgets/webgl/webgl.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +TARGET = webgl +QT += core gui widgets webenginewidgets +CONFIG += c++11 +SOURCES += main.cpp + diff --git a/tests/manual/widgets/widgets.pro b/tests/manual/widgets/widgets.pro index f06d3e1c3..34e88f0e3 100644 --- a/tests/manual/widgets/widgets.pro +++ b/tests/manual/widgets/widgets.pro @@ -1,4 +1,5 @@ TEMPLATE= subdirs SUBDIRS += \ - inputmethods + inputmethods \ + webgl diff --git a/tests/quicktestbrowser/ApplicationRoot.qml b/tests/quicktestbrowser/ApplicationRoot.qml index 980016535..e2248e350 100644 --- a/tests/quicktestbrowser/ApplicationRoot.qml +++ b/tests/quicktestbrowser/ApplicationRoot.qml @@ -53,6 +53,7 @@ QtObject { var newWindow = browserWindowComponent.createObject(root) newWindow.currentWebView.profile = profile profile.downloadRequested.connect(newWindow.onDownloadRequested) + profile.userNotification.connect(newWindow.onUserNotification) return newWindow } function createDialog(profile) { diff --git a/tests/quicktestbrowser/BrowserWindow.qml b/tests/quicktestbrowser/BrowserWindow.qml index 22f98e1c5..381e9c142 100644 --- a/tests/quicktestbrowser/BrowserWindow.qml +++ b/tests/quicktestbrowser/BrowserWindow.qml @@ -505,6 +505,18 @@ ApplicationWindow { download.accept() } + MessageDialog { + id: notificationDialog + width: 200 + standardButtons: StandardButton.Ok + } + + function onUserNotification(notification) { + notificationDialog.title = notification.title + notificationDialog.text = notification.origin.toString() + '\n' + notification.message + notificationDialog.open() + } + ZoomController { id: zoomController y: parent.mapFromItem(currentWebView, 0 , 0).y - 4 diff --git a/tests/quicktestbrowser/FeaturePermissionBar.qml b/tests/quicktestbrowser/FeaturePermissionBar.qml index 9c0b25966..500d13206 100644 --- a/tests/quicktestbrowser/FeaturePermissionBar.qml +++ b/tests/quicktestbrowser/FeaturePermissionBar.qml @@ -40,10 +40,24 @@ Rectangle { visible: false height: acceptButton.height + 4 - onRequestedFeatureChanged: { - message.text = securityOrigin + " wants to access " + message.textForFeature(requestedFeature); + + function textForFeature(feature) { + switch (feature) { + case WebEngineView.Geolocation: return 'Allow %1 to access your location information?' + case WebEngineView.MediaAudioCapture: return 'Allow %1 to access your microphone?' + case WebEngineView.MediaVideoCapture: return 'Allow %1 to access your webcam?' + case WebEngineView.MediaAudioVideoCapture: return 'Allow %1 to access your microphone and webcam?' + case WebEngineView.DesktopVideoCapture: return 'Allow %1 to capture video of your desktop?' + case WebEngineView.DesktopAudioVideoCapture: return 'Allow %1 to capture audio and video of your desktop?' + case WebEngineView.Notifications: return 'Allow %1 to show notification on your desktop?' + default: break + } + return 'Grant permission for %1 to unknown or unsupported feature [' + feature + ']?' } + onRequestedFeatureChanged: { + message.text = textForFeature(requestedFeature).arg(securityOrigin); + } RowLayout { anchors { @@ -54,17 +68,6 @@ Rectangle { Label { id: message Layout.fillWidth: true - - function textForFeature(feature) { - if (feature === WebEngineView.MediaAudioCapture) - return "your microphone" - if (feature === WebEngineView.MediaVideoCapture) - return "your camera" - if (feature === WebEngineView.MediaAudioVideoCapture) - return "your camera and microphone" - if (feature === WebEngineView.Geolocation) - return "your position" - } } Button { diff --git a/tools/buildscripts/repack_locales.py b/tools/buildscripts/repack_locales.py index 103fdaf69..8f1ae190b 100755 --- a/tools/buildscripts/repack_locales.py +++ b/tools/buildscripts/repack_locales.py @@ -56,6 +56,8 @@ INT_DIR = None # The target platform. If it is not defined, sys.platform will be used. OS = None +ENABLE_EXTENSIONS = False + # Extra input files. EXTRA_INPUT_FILES = [] @@ -109,6 +111,14 @@ def calc_inputs(locale): inputs.append(os.path.join(SHARE_INT_DIR, 'chrome', 'chromium_strings_%s.pak' % locale)) + if ENABLE_EXTENSIONS: + # For example: + # '<(SHARED_INTERMEDIATE_DIR)/extensions/strings/extensions_strings_da.pak + # TODO(jamescook): When Android stops building extensions code move this + # to the OS != 'ios' and OS != 'android' section below. + inputs.append(os.path.join(SHARE_INT_DIR, 'extensions', 'strings', + 'extensions_strings_%s.pak' % locale)) + # Add any extra input files. for extra_file in EXTRA_INPUT_FILES: inputs.append('%s_%s.pak' % (extra_file, locale)) @@ -158,6 +168,7 @@ def DoMain(argv): global INT_DIR global OS global EXTRA_INPUT_FILES + global ENABLE_EXTENSIONS parser = optparse.OptionParser("usage: %prog [options] locales") parser.add_option("-i", action="store_true", dest="inputs", default=False, @@ -177,6 +188,9 @@ def DoMain(argv): locale suffix and \".pak\" extension.") parser.add_option("-p", action="store", dest="os", help="The target OS. (e.g. mac, linux, win, etc.)") + parser.add_option("--enable-extensions", action="store", + dest="enable_extensions", + help="Whether to include strings for extensions") options, locales = parser.parse_args(argv) if not locales: @@ -188,6 +202,7 @@ def DoMain(argv): SHARE_INT_DIR = options.share_int_dir EXTRA_INPUT_FILES = options.extra_input OS = options.os + ENABLE_EXTENSIONS = options.enable_extensions == '1' if not OS: if sys.platform == 'darwin': diff --git a/tools/scripts/gn_find_mocables.py b/tools/scripts/gn_find_mocables.py index d97dcb534..d1f682456 100644 --- a/tools/scripts/gn_find_mocables.py +++ b/tools/scripts/gn_find_mocables.py @@ -32,10 +32,9 @@ import os mocables = set() includedMocs = set() -dir = sys.argv[1] files = sys.argv[2:] -for f in filter(os.path.isfile, [os.path.join(dir, f) for f in files]): +for f in filter(os.path.isfile, files): inBlockComment = False for line in open(f).readlines(): # Block comments handling diff --git a/tools/scripts/take_snapshot.py b/tools/scripts/take_snapshot.py index 4bf4381cb..4f71185e5 100755 --- a/tools/scripts/take_snapshot.py +++ b/tools/scripts/take_snapshot.py @@ -43,7 +43,7 @@ qtwebengine_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', os.chdir(qtwebengine_root) def isInGitBlacklist(file_path): - # We do need all the gyp files. + # We need all the git files. if ( '.gitignore' in file_path or '.gitmodules' in file_path or '.gitattributes' in file_path @@ -54,33 +54,14 @@ def isInChromiumBlacklist(file_path): # Filter out empty submodule directories. if (os.path.isdir(file_path)): return True - # We do need all the gyp files. - if file_path.endswith('.gyp') or file_path.endswith('.gypi') or file_path.endswith('.isolate'): - return False - # We do need all the gn file. + # We do need all the gn files. if file_path.endswith('.gn') or file_path.endswith('.gni') or file_path.endswith('.typemap') or \ file_path.endswith('.mojom'): return False - if ( '_jni' in file_path - or 'jni_' in file_path - or 'testdata/' in file_path - or '/tests/' in file_path - or ('/test/' in file_path and - not '/webrtc/' in file_path and - not file_path.startswith('net/test/') and - not file_path.endswith('mock_chrome_application_mac.h') and - not file_path.endswith('perftimer.h') and - not file_path.endswith('test-torque.tq') and - not 'ozone' in file_path and - not 'fontconfig_util_linux' in file_path and - not 'core/mojo/test/' in file_path and - not file_path.startswith('extensions/browser/')) - or file_path.endswith('.java') - or file_path.startswith('android_webview') + if (file_path.startswith('android_webview') or file_path.startswith('apps/') or file_path.startswith('ash/') or file_path.startswith('base/android') - or file_path.startswith('breakpad') or file_path.startswith('buildtools/clang_format/script') or file_path.startswith('buildtools/third_party/libc++') or file_path.startswith('buildtools/third_party/libc++abi') @@ -98,6 +79,7 @@ def isInChromiumBlacklist(file_path): not 'media/webrtc/desktop_media_list.h' in file_path and not 'media/webrtc/desktop_streams_registry.' in file_path and not 'browser/net/chrome_mojo_proxy_resolver_factory.' in file_path and + not '/browser/accessibility/' in file_path and not '/browser/custom_handlers/' in file_path and not '/browser/devtools/' in file_path and not '/browser/ui/webui/' in file_path and @@ -122,25 +104,27 @@ def isInChromiumBlacklist(file_path): not file_path.endswith('.grdp') and not file_path.endswith('.json') and not file_path.endswith('chrome_version.rc.version')) - or file_path.startswith('chrome_frame') + or file_path.startswith('chrome_elf') or file_path.startswith('chromecast') or file_path.startswith('chromeos') or file_path.startswith('cloud_print') - or file_path.startswith('components/chrome_apps/') - or file_path.startswith('components/cronet/') - or file_path.startswith('components/drive/') - or file_path.startswith('components/invalidation/') - or file_path.startswith('components/gcm_driver/') - or file_path.startswith('components/nacl/') - or file_path.startswith('components/omnibox/') - or file_path.startswith('components/policy/') - or file_path.startswith('components/proximity_auth/') - or (file_path.startswith('components/resources/terms/') and not file_path.endswith('terms_chromium.html')) - or file_path.startswith('components/rlz/') - or file_path.startswith('components/sync/') and not file_path.endswith('ordinal.h') - or file_path.startswith('components/test/') - or file_path.startswith('components/test_runner/') - or file_path.startswith('components/translate/') + or (file_path.startswith('components/') and ( + file_path.startswith('components/chrome_apps/') + or file_path.startswith('components/cronet/') + or file_path.startswith('components/drive/') + or file_path.startswith('components/invalidation/') + or file_path.startswith('components/gcm_driver/') + or file_path.startswith('components/nacl/') + or file_path.startswith('components/omnibox/') + or file_path.startswith('components/policy/') + or file_path.startswith('components/proximity_auth/') + or (file_path.startswith('components/resources/terms/') and not file_path.endswith('terms_chromium.html')) + or file_path.startswith('components/rlz/') + or file_path.startswith('components/sync/') and not file_path.endswith('ordinal.h') + or file_path.startswith('components/test/') + or file_path.startswith('components/test_runner/') + or file_path.startswith('components/translate/') + )) or file_path.startswith('content/public/android/java') or (file_path.startswith('content/shell') and not file_path.startswith('content/shell/common') and @@ -149,7 +133,7 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('google_update') or file_path.startswith('ios') or file_path.startswith('media/base/android/java') - or file_path.startswith('native_client') + or file_path.startswith('native_client_sdk') or file_path.startswith('net/android/java') or (file_path.startswith('net/data/') and '_unittest/' in file_path) or file_path.startswith('net/data/fuzzer_data/') @@ -157,86 +141,85 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('rlz') or file_path.startswith('testing/android') or file_path.startswith('testing/buildbot') - or file_path.startswith('third_party/WebKit/LayoutTests') - or file_path.startswith('third_party/WebKit/ManualTests') - or file_path.startswith('third_party/WebKit/Source/core/testing/data/') - or file_path.startswith('third_party/WebKit/Source/devtools/devtools-node-modules') - or file_path.startswith('third_party/WebKit/PerformanceTests') - or file_path.startswith('third_party/accessibility-audit') - or file_path.startswith('third_party/afl') - or file_path.startswith('third_party/android_') - or file_path.startswith('third_party/apache-win32') - or file_path.startswith('third_party/apple_sample_code') - or file_path.startswith('third_party/ashmem') - or file_path.startswith('third_party/binutils') - or file_path.startswith('third_party/bison') - or file_path.startswith('third_party/blink/perf_tests/') - or file_path.startswith('third_party/breakpad/src/processor/testdata/') - or file_path.startswith('third_party/boringssl/crypto_test_data.cc') - or file_path.startswith('third_party/boringssl/src/fuzz') - or (file_path.startswith('third_party/cacheinvalidation') and - not file_path.endswith('isolate')) - or file_path.startswith('third_party/catapult') - or file_path.startswith('third_party/chromite') - or file_path.startswith('third_party/cld_2') - or file_path.startswith('third_party/closure_compiler') - or file_path.startswith('third_party/codesighs') - or file_path.startswith('third_party/colorama') - or file_path.startswith('third_party/cygwin') - or file_path.startswith('third_party/cython') - or file_path.startswith('third_party/deqp') - or file_path.startswith('third_party/depot_tools') - or file_path.startswith('third_party/elfutils') - or file_path.startswith('third_party/freetype-android') - or file_path.startswith('third_party/google_input_tools') - or file_path.startswith('third_party/gperf') - or file_path.startswith('third_party/gnu_binutils') - or file_path.startswith('third_party/grpc') - or file_path.startswith('third_party/gtk+') - or file_path.startswith('third_party/google_appengine_cloudstorage') - or file_path.startswith('third_party/google_toolbox_for_mac') - or file_path.startswith('third_party/hunspell_dictionaries') - or (file_path.startswith('third_party/icu') and file_path.endswith('icudtl_dat.S')) - or file_path.startswith('third_party/icu/android') - or file_path.startswith('third_party/icu/ios') - or file_path.startswith('third_party/instrumented_libraries') - or file_path.startswith('third_party/jsr-305') - or file_path.startswith('third_party/junit') - or file_path.startswith('third_party/lcov') - or file_path.startswith('third_party/libphonenumber') - or file_path.startswith('third_party/libaddressinput/src/testdata') - or file_path.startswith('third_party/libaddressinput/src/common/src/test') - or file_path.startswith('third_party/libc++') - or file_path.startswith('third_party/liblouis') - or file_path.startswith('third_party/lighttpd') - or file_path.startswith('third_party/libwebm/source/webm_parser/fuzzing') - or file_path.startswith('third_party/logilab') - or file_path.startswith('third_party/markdown') - or file_path.startswith('third_party/mingw-w64') - or file_path.startswith('third_party/nacl_sdk_binaries') - or (file_path.startswith('third_party/polymer') and - not file_path.startswith('third_party/polymer/v1_0/components-chromium/')) - or file_path.startswith('third_party/openh264/src/res') - or file_path.startswith('third_party/pdfium/testing/resources') - or file_path.startswith('third_party/pdfium/tools') - or file_path.startswith('third_party/pdfsqueeze') - or file_path.startswith('third_party/pefile') - or file_path.startswith('third_party/perl') - or file_path.startswith('third_party/psyco_win32') - or file_path.startswith('third_party/pylint') - or file_path.startswith('third_party/scons-2.0.1') - or file_path.startswith('third_party/sfntly/src/cpp/data/fonts') - or file_path.startswith('third_party/sfntly/src/java') - or file_path.startswith('third_party/skia/infra') - or file_path.startswith('third_party/speech-dispatcher') - or file_path.startswith('third_party/talloc') - or file_path.startswith('third_party/trace-viewer') - or file_path.startswith('third_party/undoview') - or file_path.startswith('third_party/wayland') - or file_path.startswith('third_party/webgl') - or file_path.startswith('third_party/webrtc/resources/') - or file_path.startswith('third_party/webrtc/third_party/boringssl/crypto_test_data.cc') - or file_path.startswith('third_party/webrtc/third_party/boringssl/src/fuzz') + or (file_path.startswith('third_party/') and ( + file_path.startswith('third_party/WebKit/LayoutTests') + or file_path.startswith('third_party/accessibility-audit') + or file_path.startswith('third_party/afl') + or file_path.startswith('third_party/android_') + or file_path.startswith('third_party/apache-win32') + or file_path.startswith('third_party/apple_sample_code') + or file_path.startswith('third_party/ashmem') + or file_path.startswith('third_party/binutils') + or file_path.startswith('third_party/bison') + or file_path.startswith('third_party/blink/perf_tests/') + or file_path.startswith('third_party/breakpad/src/processor/testdata/') + or file_path.startswith('third_party/boringssl/crypto_test_data.cc') + or file_path.startswith('third_party/boringssl/src/fuzz') + or (file_path.startswith('third_party/cacheinvalidation') and + not file_path.endswith('isolate')) + or file_path.startswith('third_party/catapult') + or file_path.startswith('third_party/chromite') + or file_path.startswith('third_party/cld_2') + or file_path.startswith('third_party/closure_compiler') + or file_path.startswith('third_party/codesighs') + or file_path.startswith('third_party/colorama') + or file_path.startswith('third_party/cygwin') + or file_path.startswith('third_party/cython') + or file_path.startswith('third_party/deqp') + or file_path.startswith('third_party/depot_tools') + or file_path.startswith('third_party/elfutils') + or file_path.startswith('third_party/freetype-android') + or file_path.startswith('third_party/google_input_tools') + or file_path.startswith('third_party/gperf') + or file_path.startswith('third_party/gnu_binutils') + or file_path.startswith('third_party/grpc') + or file_path.startswith('third_party/gtk+') + or file_path.startswith('third_party/google_appengine_cloudstorage') + or file_path.startswith('third_party/google_toolbox_for_mac') + or file_path.startswith('third_party/hunspell_dictionaries') + or (file_path.startswith('third_party/icu') and file_path.endswith('icudtl_dat.S')) + or file_path.startswith('third_party/icu/android') + or file_path.startswith('third_party/icu/ios') + or file_path.startswith('third_party/instrumented_libraries') + or file_path.startswith('third_party/jsr-305') + or file_path.startswith('third_party/junit') + or file_path.startswith('third_party/lcov') + or file_path.startswith('third_party/libphonenumber') + or file_path.startswith('third_party/libaddressinput/src/testdata') + or file_path.startswith('third_party/libaddressinput/src/common/src/test') + or file_path.startswith('third_party/libc++') + or file_path.startswith('third_party/liblouis') + or file_path.startswith('third_party/lighttpd') + or file_path.startswith('third_party/libwebm/source/webm_parser/fuzzing') + or file_path.startswith('third_party/logilab') + or file_path.startswith('third_party/markdown') + or file_path.startswith('third_party/mingw-w64') + or file_path.startswith('third_party/nacl_sdk_binaries') + or (file_path.startswith('third_party/polymer') and + not file_path.startswith('third_party/polymer/v1_0/components-chromium/')) + or file_path.startswith('third_party/openh264/src/res') + or file_path.startswith('third_party/pdfium/testing/resources') + or file_path.startswith('third_party/pdfium/tools') + or file_path.startswith('third_party/pdfsqueeze') + or file_path.startswith('third_party/pefile') + or file_path.startswith('third_party/perl') + or file_path.startswith('third_party/psyco_win32') + or file_path.startswith('third_party/pylint') + or file_path.startswith('third_party/scons-2.0.1') + or file_path.startswith('third_party/sfntly/src/cpp/data/fonts') + or file_path.startswith('third_party/sfntly/src/java') + or file_path.startswith('third_party/skia/infra') + or file_path.startswith('third_party/speech-dispatcher') + or file_path.startswith('third_party/swiftshader/third_party/llvm') + or file_path.startswith('third_party/talloc') + or file_path.startswith('third_party/trace-viewer') + or file_path.startswith('third_party/undoview') + or file_path.startswith('third_party/wayland') + or file_path.startswith('third_party/webgl') + or file_path.startswith('third_party/webrtc/resources/') + or file_path.startswith('third_party/webrtc/third_party/boringssl/crypto_test_data.cc') + or file_path.startswith('third_party/webrtc/third_party/boringssl/src/fuzz') + )) or file_path.startswith('tools/android') or file_path.startswith('tools/luci_go') or file_path.startswith('tools/memory_inspector') @@ -250,7 +233,19 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('ui/events/ozone/chromeos') or file_path.startswith('ui/file_manager') or file_path.startswith('ui/gfx/chromeos') - + or 'testdata/' in file_path + or '/tests/' in file_path + or ('/test/' in file_path and + not '/webrtc/' in file_path and + not file_path.startswith('net/test/') and + not file_path.endswith('mock_chrome_application_mac.h') and + not file_path.endswith('perftimer.h') and + not file_path.endswith('test-torque.tq') and + not 'ozone' in file_path and + not 'clang_coverage' in file_path and + not 'fontconfig_util_linux' in file_path and + not 'core/mojo/test/' in file_path and + not file_path.startswith('extensions/browser/')) ): return True return False @@ -342,6 +337,7 @@ def exportChromium(): files = listFilesInCurrentRepository() # Add LASTCHANGE files which are not tracked by git. files.append('build/util/LASTCHANGE') + files.append('build/util/LASTCHANGE.committime') files.append('skia/ext/skia_commit_hash.h') files.append('gpu/config/gpu_lists_version.h') print 'copying files to ' + third_party_chromium diff --git a/tools/scripts/version_resolver.py b/tools/scripts/version_resolver.py index 27062fbcf..32c3f9d12 100644 --- a/tools/scripts/version_resolver.py +++ b/tools/scripts/version_resolver.py @@ -38,8 +38,8 @@ import json import urllib2 import git_submodule as GitSubmodule -chromium_version = '69.0.3497.113' -chromium_branch = '3497' +chromium_version = '71.0.3578.93' +chromium_branch = '3578' ninja_version = 'v1.8.2' json_url = 'http://omahaproxy.appspot.com/all.json' @@ -51,7 +51,6 @@ upstream_src_dir = os.path.abspath(snapshot_src_dir + '_upstream') submodule_blacklist = [ 'third_party/WebKit/LayoutTests/w3c/csswg-test' , 'third_party/WebKit/LayoutTests/w3c/web-platform-tests' - , 'third_party/jsoncpp/source' , 'chrome/tools/test/reference_build/chrome_mac' , 'chrome/tools/test/reference_build/chrome_linux' , 'chrome/tools/test/reference_build/chrome_win' |