diff options
34 files changed, 431 insertions, 124 deletions
diff --git a/dependencies.yaml b/dependencies.yaml index 0c1d011bf..a6dd5cb7e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../qtdeclarative: - ref: c63bb2bad5b4e741ed8a1e16d8f1f916c9baf61d + ref: 7bdeea2c309150c8b49558b135232926d6a89c50 required: true ../qtpositioning: - ref: 152d0039c7ad2898e8e5b122420f59049536143b + ref: d09694ea15c235cec07dc5e7c0073c6f4b13f5ea required: false ../qttools: - ref: 0d80d76bf14905204a248655cd88fe6cfd5706db + ref: b22644dd731bd2c2312d31d08a80fd637f0c1e3a required: false ../qtwebchannel: - ref: 2a194a801a86d3d9342b47ccba9a1105aadf70d6 + ref: b9d1a08e490fc6831a26d0c45ecf56cef11968a0 required: false diff --git a/examples/webenginequick/quicknanobrowser/BrowserWindow.qml b/examples/webenginequick/quicknanobrowser/BrowserWindow.qml index 3b911262b..e2cb71fb3 100644 --- a/examples/webenginequick/quicknanobrowser/BrowserWindow.qml +++ b/examples/webenginequick/quicknanobrowser/BrowserWindow.qml @@ -563,6 +563,11 @@ ApplicationWindow { settings.imageAnimationPolicy: appSettings.imageAnimationPolicy onCertificateError: function(error) { + if (!error.isMainFrame) { + error.rejectCertificate(); + return; + } + error.defer(); sslDialog.enqueue(error); } diff --git a/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc b/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc index 1dc209c2e..1e00402ff 100644 --- a/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc +++ b/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc @@ -75,13 +75,16 @@ \section1 Handling Certificate Errors - If the certificate of the site being loaded triggers a certificate error, we call the - \l{WebEngineCertificateError::}{defer()} QML method to pause the URL request and wait for user - input: + In case of a certificate error, we check whether it came from the main frame, or from a + resource inside the page. Resource errors automatically trigger a certificate rejection, + since a user won't have enough context to make a decision. + For all other cases, we call the \l{WebEngineCertificateError::}{defer()} QML method to pause + the URL request and wait for user input: \quotefromfile webenginequick/quicknanobrowser/BrowserWindow.qml \skipto onCertificateError \printuntil } + \printuntil } We use the Dialog type to prompt users to continue or cancel the loading of the web page. If users select \uicontrol Yes, we call the diff --git a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc index 211535204..a312da3ad 100644 --- a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc +++ b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc @@ -223,13 +223,17 @@ The \c handleProxyAuthenticationRequired signal handler implements the very same steps for the authentication of HTTP proxies. - In case of SSL errors, we just need to return a boolean value indicating - whether the certificate should be ignored. + In case of SSL errors, we check whether they come from the main frame, or + from a resource inside the page. Resource errors automatically trigger a + certificate rejection, since a user won't have enough context to make a + decision. For all other cases, we trigger a dialog where the user can + allow or reject the certificate. \quotefromfile webenginewidgets/simplebrowser/webpage.cpp \skipto WebPage::handleCertificateError( \printuntil } \printuntil } + \printuntil } \section1 Opening a Web Page diff --git a/examples/webenginewidgets/simplebrowser/webpage.cpp b/examples/webenginewidgets/simplebrowser/webpage.cpp index 807cdf0ff..6fc9aeaf9 100644 --- a/examples/webenginewidgets/simplebrowser/webpage.cpp +++ b/examples/webenginewidgets/simplebrowser/webpage.cpp @@ -19,6 +19,13 @@ WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) void WebPage::handleCertificateError(QWebEngineCertificateError error) { + // Automatically block certificate errors from page resources without prompting the user. + // This mirrors the behavior found in other major browsers. + if (!error.isMainFrame()) { + error.rejectCertificate(); + return; + } + error.defer(); QTimer::singleShot(0, this, [this, error]() mutable { emit createCertificateErrorDialog(error); }); diff --git a/src/core/api/qwebenginecertificateerror.cpp b/src/core/api/qwebenginecertificateerror.cpp index 90d8a542d..85c5d5127 100644 --- a/src/core/api/qwebenginecertificateerror.cpp +++ b/src/core/api/qwebenginecertificateerror.cpp @@ -88,6 +88,19 @@ QUrl QWebEngineCertificateError::url() const } /*! + \property QWebEngineCertificateError::isMainFrame + \since 6.8 + + Returns whether the certificate error comes from the main frame. If false, + the error comes from a sub-resource and most likely needs to be rejected without + user input. +*/ +bool QWebEngineCertificateError::isMainFrame() const +{ + return d->isMainFrame(); +} + +/*! Returns the type of the error. \sa description(), isOverridable() diff --git a/src/core/api/qwebenginecertificateerror.h b/src/core/api/qwebenginecertificateerror.h index c4a3585f4..3eef3dcca 100644 --- a/src/core/api/qwebenginecertificateerror.h +++ b/src/core/api/qwebenginecertificateerror.h @@ -24,6 +24,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineCertificateError Q_PROPERTY(Type type READ type CONSTANT FINAL) Q_PROPERTY(QString description READ description CONSTANT FINAL) Q_PROPERTY(bool overridable READ isOverridable CONSTANT FINAL) + Q_PROPERTY(bool isMainFrame READ isMainFrame CONSTANT FINAL REVISION(6, 8)) public: QWebEngineCertificateError(const QWebEngineCertificateError &other); @@ -57,6 +58,7 @@ public: QUrl url() const; bool isOverridable() const; QString description() const; + bool isMainFrame() const; Q_INVOKABLE void defer(); Q_INVOKABLE void rejectCertificate(); diff --git a/src/core/api/qwebengineframe.cpp b/src/core/api/qwebengineframe.cpp index edd89d663..1eedc4b92 100644 --- a/src/core/api/qwebengineframe.cpp +++ b/src/core/api/qwebengineframe.cpp @@ -3,6 +3,9 @@ #include "qwebengineframe.h" +#include "qwebenginescript.h" +#include <QtQml/qqmlengine.h> + #include "web_contents_adapter_client.h" #include "web_contents_adapter.h" @@ -101,6 +104,77 @@ QSizeF QWebEngineFrame::size() const return m_adapterClient->webContentsAdapter()->frameSize(m_id); } +/*! \fn void QWebEngineFrame::runJavaScript(const QString &script, const std::function<void(const QVariant &)> &callback) + \fn void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId) + \fn void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId, const + std::function<void(const QVariant &)> &callback) + + Runs the JavaScript code contained in \a script on this frame, without checking + whether the DOM of the page has been constructed. + To avoid conflicts with other scripts executed on the page, the world in + which the script is run is specified by \a worldId. The world ID values are + the same as provided by QWebEngineScript::ScriptWorldId, and between \c 0 + and \c 256. If you leave out the \c world ID, the script is run in the + \c MainWorld. + When the script has been executed, \a callback is called with the result of the last + executed statement. \c callback can be any of a function pointer, a functor or a lambda, + and it is expected to take a QVariant parameter. For example: + \code + page.runJavaScript("document.title", [](const QVariant &v) { qDebug() << v.toString(); }); + \endcode + Only plain data can be returned from JavaScript as the result value. + Supported data types include all of the JSON data types as well as, for + example, \c{Date} and \c{ArrayBuffer}. Unsupported data types include, for + example, \c{Function} and \c{Promise}. + \warning Do not execute lengthy routines in the callback function, because it might block the + rendering of the web engine page. + \warning We guarantee that the \a callback is always called, but it might be + done during page destruction. When QWebEnginePage is deleted, the callback is triggered with an + invalid value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView + instance inside it. + \sa QWebEngineScript::ScriptWorldId, QWebEnginePage::runJavaScript, {Script Injection} + */ +void QWebEngineFrame::runJavaScript(const QString &script, + const std::function<void(const QVariant &)> &callback) +{ + runJavaScript(script, QWebEngineScript::MainWorld, callback); +} + +void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId, + const std::function<void(const QVariant &)> &callback) +{ + m_adapterClient->runJavaScript(script, worldId, m_id, callback); +} + +void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId) +{ + runJavaScript(script, worldId, std::function<void(const QVariant &)>{}); +} + +void QWebEngineFrame::runJavaScript(const QString &script, const QJSValue &callback) +{ + runJavaScript(script, QWebEngineScript::MainWorld, callback); +} + +void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId, + const QJSValue &callback) +{ + std::function<void(const QVariant &)> wrappedCallback; + if (!callback.isUndefined()) { + const QObject *holdingObject = m_adapterClient->holdingQObject(); + wrappedCallback = [holdingObject, callback](const QVariant &result) { + if (auto engine = qmlEngine(holdingObject)) { + QJSValueList args; + args.append(engine->toScriptValue(result)); + callback.call(args); + } else { + qWarning("No QML engine found to execute runJavaScript() callback"); + } + }; + } + runJavaScript(script, worldId, wrappedCallback); +} + /*! \fn bool QWebEngineFrame::operator==(const QWebEngineFrame &left, const QWebEngineFrame &right) noexcept Returns \c{true} if \a left and \a right represent the same frame in the same web page, diff --git a/src/core/api/qwebengineframe.h b/src/core/api/qwebengineframe.h index e58961848..988d50d8e 100644 --- a/src/core/api/qwebengineframe.h +++ b/src/core/api/qwebengineframe.h @@ -6,6 +6,7 @@ #include <QtWebEngineCore/qtwebenginecoreglobal.h> #include <QtQml/qqmlregistration.h> +#include <QtQml/qjsvalue.h> #include <QtCore/qcompare.h> #include <QtCore/QList> #include <QtCore/QSizeF> @@ -39,6 +40,15 @@ public: QUrl url() const; QSizeF size() const; + void runJavaScript(const QString &script, + const std::function<void(const QVariant &)> &callback); + void runJavaScript(const QString &script, quint32 worldId, + const std::function<void(const QVariant &)> &callback); + Q_INVOKABLE void runJavaScript(const QString &script, quint32 worldId = 0); + Q_INVOKABLE void runJavaScript(const QString &script, const QJSValue &callback); + Q_INVOKABLE void runJavaScript(const QString &script, quint32 worldId, + const QJSValue &callback); + friend inline bool comparesEqual(const QWebEngineFrame &lhs, const QWebEngineFrame &rhs) noexcept { diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp index c2a737d72..439214235 100644 --- a/src/core/api/qwebenginepage.cpp +++ b/src/core/api/qwebenginepage.cpp @@ -199,6 +199,12 @@ void QWebEnginePagePrivate::iconChanged(const QUrl &url) Q_EMIT q->iconChanged(iconUrl.isEmpty() ? QIcon() : adapter->icon()); } +void QWebEnginePagePrivate::zoomFactorChanged(qreal factor) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->zoomFactorChanged(factor); +} + void QWebEnginePagePrivate::loadProgressChanged(int progress) { Q_Q(QWebEnginePage); @@ -489,10 +495,16 @@ void QWebEnginePagePrivate::windowCloseRejected() // Do nothing for now. } -void QWebEnginePagePrivate::didRunJavaScript(quint64 requestId, const QVariant& result) +void QWebEnginePagePrivate::runJavaScript(const QString &script, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) { - if (auto callback = m_variantCallbacks.take(requestId)) - callback(result); + ensureInitialized(); + if (adapter->lifecycleState() == WebContentsAdapter::LifecycleState::Discarded) { + qWarning("runJavaScript: disabled in Discarded state"); + if (callback) + callback(QVariant()); + } else + adapter->runJavaScript(script, worldId, frameId, callback); } void QWebEnginePagePrivate::didFetchDocumentMarkup(quint64 requestId, const QString& result) @@ -971,11 +983,9 @@ QWebEnginePage::~QWebEnginePage() setDevToolsPage(nullptr); emit _q_aboutToDelete(); - for (auto varFun : std::as_const(d_ptr->m_variantCallbacks)) - varFun(QVariant()); + d_ptr->adapter->clearJavaScriptCallbacks(); for (auto strFun : std::as_const(d_ptr->m_stringCallbacks)) strFun(QString()); - d_ptr->m_variantCallbacks.clear(); d_ptr->m_stringCallbacks.clear(); } } @@ -2020,7 +2030,7 @@ void QWebEnginePage::setZoomFactor(qreal factor) Q_D(QWebEnginePage); d->defaultZoomFactor = factor; - if (d->adapter->isInitialized()) { + if (d->adapter->isInitialized() && !qFuzzyCompare(factor, zoomFactor())) { d->adapter->setZoomFactor(factor); // MEMO: should reset if factor was not applied due to being invalid d->defaultZoomFactor = zoomFactor(); @@ -2029,40 +2039,13 @@ void QWebEnginePage::setZoomFactor(qreal factor) void QWebEnginePage::runJavaScript(const QString& scriptSource, const std::function<void(const QVariant &)> &resultCallback) { - Q_D(QWebEnginePage); - d->ensureInitialized(); - if (d->adapter->lifecycleState() == WebContentsAdapter::LifecycleState::Discarded) { - qWarning("runJavaScript: disabled in Discarded state"); - if (resultCallback) - resultCallback(QVariant()); - return; - } - quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, QWebEngineScript::MainWorld); - if (requestId) - d->m_variantCallbacks.insert(requestId, resultCallback); - else if (resultCallback) - resultCallback(QVariant()); + runJavaScript(scriptSource, QWebEngineScript::MainWorld, resultCallback); } void QWebEnginePage::runJavaScript(const QString& scriptSource, quint32 worldId, const std::function<void(const QVariant &)> &resultCallback) { Q_D(QWebEnginePage); - d->ensureInitialized(); - if (d->adapter->lifecycleState() == WebContentsAdapter::LifecycleState::Discarded) { - qWarning("runJavaScript: disabled in Discarded state"); - if (resultCallback) - resultCallback(QVariant()); - return; - } - if (resultCallback) { - quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, worldId); - if (requestId) - d->m_variantCallbacks.insert(requestId, resultCallback); - else - resultCallback(QVariant()); - } else { - d->adapter->runJavaScript(scriptSource, worldId); - } + d->runJavaScript(scriptSource, worldId, WebContentsAdapter::kUseMainFrameId, resultCallback); } /*! diff --git a/src/core/api/qwebenginepage.h b/src/core/api/qwebenginepage.h index e5a4e9551..13c582931 100644 --- a/src/core/api/qwebenginepage.h +++ b/src/core/api/qwebenginepage.h @@ -51,7 +51,7 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject Q_PROPERTY(QString selectedText READ selectedText) Q_PROPERTY(bool hasSelection READ hasSelection) Q_PROPERTY(QUrl requestedUrl READ requestedUrl) - Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor) + Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged) Q_PROPERTY(QString title READ title NOTIFY titleChanged) Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) Q_PROPERTY(QUrl iconUrl READ iconUrl NOTIFY iconUrlChanged) @@ -344,6 +344,7 @@ Q_SIGNALS: void iconUrlChanged(const QUrl &url); void iconChanged(const QIcon &icon); + void zoomFactorChanged(qreal factor); void scrollPositionChanged(const QPointF &position); void contentsSizeChanged(const QSizeF &size); void audioMutedChanged(bool muted); diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h index 31ace7e9d..f0e179ea8 100644 --- a/src/core/api/qwebenginepage_p.h +++ b/src/core/api/qwebenginepage_p.h @@ -102,6 +102,7 @@ public: void titleChanged(const QString &) override; void urlChanged() override; void iconChanged(const QUrl &) override; + void zoomFactorChanged(qreal factor) override; void loadProgressChanged(int progress) override; void didUpdateTargetURL(const QUrl &) override; void selectionChanged() override; @@ -130,7 +131,8 @@ public: void javascriptDialog(QSharedPointer<QtWebEngineCore::JavaScriptDialogController>) override; void runFileChooser(QSharedPointer<QtWebEngineCore::FilePickerController>) override; void showColorDialog(QSharedPointer<QtWebEngineCore::ColorChooserController>) override; - void didRunJavaScript(quint64 requestId, const QVariant &result) override; + void runJavaScript(const QString &script, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) override; void didFetchDocumentMarkup(quint64 requestId, const QString &result) override; void didFetchDocumentInnerText(quint64 requestId, const QString &result) override; void didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) override; @@ -214,7 +216,6 @@ public: QPrinter *currentPrinter = nullptr; #endif - mutable QMap<quint64, std::function<void(const QVariant &)>> m_variantCallbacks; mutable QMap<quint64, std::function<void(const QString &)>> m_stringCallbacks; QMap<quint64, std::function<void(const QByteArray &)>> m_pdfResultCallbacks; mutable QAction *actions[QWebEnginePage::WebActionCount]; diff --git a/src/core/certificate_error_controller.cpp b/src/core/certificate_error_controller.cpp index 014d74500..51f9f3edb 100644 --- a/src/core/certificate_error_controller.cpp +++ b/src/core/certificate_error_controller.cpp @@ -77,10 +77,12 @@ static int IsCertErrorFatal(int cert_error) CertificateErrorController::CertificateErrorController( int cert_error, const net::SSLInfo &ssl_info, const GURL &request_url, - bool strict_enforcement, base::OnceCallback<void(content::CertificateRequestResultType)> cb) + bool main_frame, bool strict_enforcement, + base::OnceCallback<void(content::CertificateRequestResultType)> cb) : m_certError(QWebEngineCertificateError::Type(cert_error)) , m_requestUrl(toQt(request_url)) , m_overridable(!IsCertErrorFatal(cert_error) && !strict_enforcement) + , m_mainFrame(main_frame) { // MEMO set callback anyway even for non overridable error since chromium halts load until it's called // callback will be executed either explicitly by use code or implicitly when error goes out of scope @@ -204,4 +206,9 @@ QList<QSslCertificate> CertificateErrorController::certificateChain() const return m_certificateChain; } +bool CertificateErrorController::isMainFrame() const +{ + return m_mainFrame; +} + } diff --git a/src/core/certificate_error_controller.h b/src/core/certificate_error_controller.h index cbcb60f8a..b0d38fd57 100644 --- a/src/core/certificate_error_controller.h +++ b/src/core/certificate_error_controller.h @@ -37,7 +37,7 @@ class Q_WEBENGINECORE_EXPORT CertificateErrorController { public: CertificateErrorController( int cert_error, const net::SSLInfo &ssl_info, const GURL &request_url, - bool strict_enforcement, + bool main_frame, bool strict_enforcement, base::OnceCallback<void(content::CertificateRequestResultType)> callback); ~CertificateErrorController(); @@ -47,6 +47,7 @@ public: QString errorString() const; QDateTime validExpiry() const; QList<QSslCertificate> certificateChain() const; + bool isMainFrame() const; bool deferred() const; void defer(); @@ -65,6 +66,7 @@ public: bool m_overridable; base::OnceCallback<void(content::CertificateRequestResultType)> m_callback; QList<QSslCertificate> m_certificateChain; + bool m_mainFrame; bool m_answered = false, m_deferred = false; diff --git a/src/core/compositor/native_skia_output_device_direct3d11.cpp b/src/core/compositor/native_skia_output_device_direct3d11.cpp index 352fa9f92..2f1ed5f61 100644 --- a/src/core/compositor/native_skia_output_device_direct3d11.cpp +++ b/src/core/compositor/native_skia_output_device_direct3d11.cpp @@ -56,6 +56,7 @@ QSGTexture *NativeSkiaOutputDeviceDirect3D11::texture(QQuickWindow *win, uint32_ status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle); Q_ASSERT(status == S_OK); Q_ASSERT(sharedHandle); + resource->Release(); // Pass texture between two D3D devices: ID3D11Device1 *device = static_cast<ID3D11Device1 *>( diff --git a/src/core/compositor/native_skia_output_device_vulkan.cpp b/src/core/compositor/native_skia_output_device_vulkan.cpp index c2ad7a382..f51eba985 100644 --- a/src/core/compositor/native_skia_output_device_vulkan.cpp +++ b/src/core/compositor/native_skia_output_device_vulkan.cpp @@ -187,6 +187,7 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te Q_ASSERT(status == S_OK); status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle); Q_ASSERT(status == S_OK); + resource->Release(); if (!sharedHandle) qFatal("VULKAN: Unable to extract shared handle."); diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 7192edbc2..28ed6ef7c 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -276,14 +276,14 @@ void ContentBrowserClientQt::AllowCertificateError(content::WebContents *webCont int cert_error, const net::SSLInfo &ssl_info, const GURL &request_url, - bool /* is_main_frame_request */, + bool is_main_frame_request, bool strict_enforcement, base::OnceCallback<void(content::CertificateRequestResultType)> callback) { WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); QSharedPointer<CertificateErrorController> errorController(new CertificateErrorController( - cert_error, ssl_info, request_url, strict_enforcement, std::move(callback))); + cert_error, ssl_info, request_url, is_main_frame_request, strict_enforcement, std::move(callback))); contentsDelegate->allowCertificateError(errorController); } diff --git a/src/core/doc/src/qtwebengine-overview.qdoc b/src/core/doc/src/qtwebengine-overview.qdoc index 6eccc669e..3a5d30338 100644 --- a/src/core/doc/src/qtwebengine-overview.qdoc +++ b/src/core/doc/src/qtwebengine-overview.qdoc @@ -44,8 +44,13 @@ dedicated profile for a \e {private browsing} mode, where no information is permanently saved. \note The \QWE Widgets module uses the \l{Qt Quick Scene Graph}{Qt Quick scene graph} - to compose the elements of a web page into one view. This means that the UI process - requires OpenGL ES 2.0 or OpenGL 2.0 for its rendering. + to compose the elements of a web page into one view. + + The content is rendered using the graphics card (GPU) capabilities. The scene + graph, in turn, relies on the Qt Rendering Hardware Interface as an abstraction + layer for the different capabilities and APIs a GPU might feature. For more + advice on how to tweak the rendering pipeline, see therefore + \l{Rendering via the Qt Rendering Hardware Interface}. \section2 Qt WebEngine Module diff --git a/src/core/doc/src/qwebengine-licensing.qdoc b/src/core/doc/src/qwebengine-licensing.qdoc index 796a9664d..936ceff97 100644 --- a/src/core/doc/src/qwebengine-licensing.qdoc +++ b/src/core/doc/src/qwebengine-licensing.qdoc @@ -5,20 +5,49 @@ \group qtwebengine-licensing \title Qt WebEngine Licensing -The Qt specific parts of the \QWE module are dual-licensed -under Commercial and GNU Lesser General Public License (LGPLv3). -In addition, the module contains code licensed under LGPLv2. - -The module includes a snapshot of Chromium. As such, users need to -respect the licenses of Chromium, and third-party code included in -Chromium. The arguably most restrictive license to be respected by -all users is LGPLv2.1. +The \QWE module uses Chromium to provide most of its +functionality. During the build process, Chromium becomes +a part of the \l{Qt WebEngine Core} library. +Therefore, when distributing \QWE, users need to comply +to both the licenses of the \QWE part as developed under +the Qt Project, as well as the licenses that are part of +Chromium. + +The Qt specific parts of the \QWE module are available under +commercial licenses from The Qt Company. Alternatively, +they are available under GNU Lesser General Public License v3.0, +or GNU General Public License v3.0, or +GNU General Public License v2.0. + +The Chromium part contains code available under various +licenses, with the most restrictive license being the +\e{GNU Lesser General Public License v2.1} (LGPL 2.1). +For a full list, see \l{Third-Party Licenses} below. + +\section1 Complying with LGPL 2.1 + +The Qt Company allows commercial customers to +distribute the source code of the \QWE module alongside the +\QWE binaries, and allows recipients to customize and build +\QWE from sources. This includes building and using +such a modified \QWE with an application or system using +an otherwise commercially licensed Qt. + +The Qt Company also keeps the Qt specific parts of the \QWE +module available under the GNU Lesser General Public License v3.0, +or the GNU General Public License v3.0, or +the GNU General Public License v2.0, even if WebEngine is released +as part of a commercial-only Long-Term Support release (LTS, see +also \l{Long-Term Support releases}). + +\section1 Third-Party Licenses + +Third party licenses included in the sources as part of Chromium +are found below. \note Any GPL licenses listed below are only used to access Linux system -resources. \QWE does not link to nor distribute GPL binary code, and -it does not affect users of \QWE. - -Third party licenses included in the sources are: +resources. The \QWE libraries and plugins does not link to nor distribute +GPL binary code, and it does not affect users of \QWE. */ /*! diff --git a/src/core/doc/src/qwebenginepage_lgpl.qdoc b/src/core/doc/src/qwebenginepage_lgpl.qdoc index c2515cd13..1640ca8be 100644 --- a/src/core/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/core/doc/src/qwebenginepage_lgpl.qdoc @@ -735,6 +735,8 @@ \brief The zoom factor for the page content. Valid values are within the range from \c{0.25} to \c{5.0}. The default factor is \c{1.0}. + + \sa zoomFactorChanged() */ /*! @@ -772,7 +774,7 @@ during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. - \sa scripts(), QWebEngineScript::ScriptWorldId, {Script Injection} + \sa scripts(), QWebEngineScript::ScriptWorldId, QWebEngineFrame::runJavaScript, {Script Injection} */ /*! @@ -836,3 +838,12 @@ \sa QWebEngineWebAuthUxRequest */ + +/*! + \fn void QWebEnginePage::zoomFactorChanged(qreal factor) + \since 6.8 + + This signal is emitted whenever the zoom \a factor for the page changes. + + \sa zoomFactor(), setZoomFactor() +*/ diff --git a/src/core/ozone/surface_factory_qt.cpp b/src/core/ozone/surface_factory_qt.cpp index 04e6ec28f..2d311a02a 100644 --- a/src/core/ozone/surface_factory_qt.cpp +++ b/src/core/ozone/surface_factory_qt.cpp @@ -226,7 +226,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle( int fd = fds[i]; int stride = strides[i]; int offset = offsets[i]; - int size = handle.planes[i].size; + int planeSize = handle.planes[i].size; if (fd == -1) { fd = fds[0]; @@ -234,7 +234,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle( offset = handle.planes[i].offset; } - gfx::NativePixmapPlane plane(stride, offset, size, base::ScopedFD(::dup(fd))); + gfx::NativePixmapPlane plane(stride, offset, planeSize, base::ScopedFD(::dup(fd))); bufferHandle.planes.push_back(std::move(plane)); } diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 6decf8780..b06ed2121 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -174,10 +174,9 @@ static QVariant fromJSValue(const base::Value *result) return ret; } -static void callbackOnEvaluateJS(WebContentsAdapterClient *adapterClient, quint64 requestId, base::Value result) +static void callbackOnEvaluateJS(WebContentsAdapter *adapter, quint64 requestId, base::Value result) { - if (requestId) - adapterClient->didRunJavaScript(requestId, fromJSValue(&result)); + adapter->didRunJavaScript(requestId, result); } #if QT_CONFIG(webengine_printing_and_pdf) @@ -993,6 +992,9 @@ void WebContentsAdapter::setZoomFactor(qreal factor) const content::GlobalRenderFrameHostId global_id = m_webContents->GetPrimaryMainFrame()->GetGlobalId(); zoomMap->SetTemporaryZoomLevel(global_id, zoomLevel); } + + if (m_adapterClient) + m_adapterClient->zoomFactorChanged(currentZoomFactor()); } qreal WebContentsAdapter::currentZoomFactor() const @@ -1039,36 +1041,67 @@ QAccessibleInterface *WebContentsAdapter::browserAccessible() } #endif // QT_CONFIG(accessibility) -void WebContentsAdapter::runJavaScript(const QString &javaScript, quint32 worldId) +content::RenderFrameHost *WebContentsAdapter::renderFrameHostFromFrameId(quint64 frameId) const { - CHECK_INITIALIZED(); - content::RenderFrameHost *rfh = m_webContents->GetPrimaryMainFrame(); - Q_ASSERT(rfh); - if (!static_cast<content::RenderFrameHostImpl*>(rfh)->GetAssociatedLocalFrame()) { - qWarning() << "Local frame is gone, not running script"; - return; + content::RenderFrameHost *result; + if (frameId == kUseMainFrameId) { + result = m_webContents->GetPrimaryMainFrame(); + } else { + auto *ftn = content::FrameTreeNode::GloballyFindByID(static_cast<int>(frameId)); + if (!ftn) + return nullptr; + + result = ftn->current_frame_host(); } - if (worldId == 0) - rfh->ExecuteJavaScript(toString16(javaScript), base::NullCallback()); - else - rfh->ExecuteJavaScriptInIsolatedWorld(toString16(javaScript), base::NullCallback(), worldId); + Q_ASSERT(result); + return result; } -quint64 WebContentsAdapter::runJavaScriptCallbackResult(const QString &javaScript, quint32 worldId) +void WebContentsAdapter::runJavaScript(const QString &javaScript, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) { - CHECK_INITIALIZED(0); - content::RenderFrameHost *rfh = m_webContents->GetPrimaryMainFrame(); - Q_ASSERT(rfh); + auto exit = [&] { + if (callback) + callback(QVariant()); + }; + + if (!isInitialized()) + return exit(); + auto *rfh = renderFrameHostFromFrameId(frameId); + if (!rfh) + return exit(); if (!static_cast<content::RenderFrameHostImpl*>(rfh)->GetAssociatedLocalFrame()) { qWarning() << "Local frame is gone, not running script"; - return 0; + return exit(); + } + + content::RenderFrameHost::JavaScriptResultCallback internalCallback = base::NullCallback(); + if (callback) { + internalCallback = base::BindOnce(&callbackOnEvaluateJS, this, m_nextRequestId); + m_javaScriptCallbacks.insert(m_nextRequestId, callback); + ++m_nextRequestId; } - content::RenderFrameHost::JavaScriptResultCallback callback = base::BindOnce(&callbackOnEvaluateJS, m_adapterClient, m_nextRequestId); if (worldId == 0) - rfh->ExecuteJavaScript(toString16(javaScript), std::move(callback)); + rfh->ExecuteJavaScript(toString16(javaScript), std::move(internalCallback)); else - rfh->ExecuteJavaScriptInIsolatedWorld(toString16(javaScript), std::move(callback), worldId); - return m_nextRequestId++; + rfh->ExecuteJavaScriptInIsolatedWorld(toString16(javaScript), std::move(internalCallback), + worldId); +} + +void WebContentsAdapter::didRunJavaScript(quint64 requestId, const base::Value &result) +{ + Q_ASSERT(requestId); + auto callback = m_javaScriptCallbacks.take(requestId); + Q_ASSERT(callback); + callback(fromJSValue(&result)); +} + +// Called when QWebEnginePage is deleted +void WebContentsAdapter::clearJavaScriptCallbacks() +{ + for (auto varFun : std::as_const(m_javaScriptCallbacks)) + varFun(QVariant()); + m_javaScriptCallbacks.clear(); } quint64 WebContentsAdapter::fetchDocumentMarkup() diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index a5cad8664..24de7eb1b 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -16,8 +16,10 @@ #define WEB_CONTENTS_ADAPTER_H #include <QtCore/QSharedPointer> +#include <QtCore/QMap> #include <QtCore/QString> #include <QtCore/QUrl> +#include <QtCore/QVariant> #include <QtCore/QPointer> #include <QtGui/qtgui-config.h> #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> @@ -27,6 +29,7 @@ #include "web_contents_adapter_client.h" +#include <functional> #include <memory> #include <optional> @@ -36,9 +39,14 @@ struct WebPreferences; } } +namespace base { +class Value; +} + namespace content { class WebContents; class SiteInstance; +class RenderFrameHost; } QT_BEGIN_NAMESPACE @@ -64,6 +72,8 @@ class WebChannelIPCTransportHost; class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<WebContentsAdapter> { public: + // Sentinel to indicate that a behavior should happen on the main frame + static constexpr quint64 kUseMainFrameId = -2; // Sentinel to indicate a frame doesn't exist, for example with `findFrameByName` static constexpr quint64 kInvalidFrameId = -3; @@ -129,8 +139,10 @@ public: void serializeNavigationHistory(QDataStream &output); void setZoomFactor(qreal); qreal currentZoomFactor() const; - void runJavaScript(const QString &javaScript, quint32 worldId); - quint64 runJavaScriptCallbackResult(const QString &javaScript, quint32 worldId); + void runJavaScript(const QString &javaScript, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback); + void didRunJavaScript(quint64 requestId, const base::Value &result); + void clearJavaScriptCallbacks(); quint64 fetchDocumentMarkup(); quint64 fetchDocumentInnerText(); void updateWebPreferences(const blink::web_pref::WebPreferences &webPreferences); @@ -230,6 +242,7 @@ private: Q_DISABLE_COPY(WebContentsAdapter) void waitForUpdateDragActionCalled(); bool handleDropDataFileContents(const content::DropData &dropData, QMimeData *mimeData); + content::RenderFrameHost *renderFrameHostFromFrameId(quint64 frameId) const; void wasShown(); void wasHidden(); @@ -255,6 +268,7 @@ private: WebContentsAdapterClient *m_adapterClient; quint64 m_nextRequestId; QMap<QUrl, bool> m_pendingMouseLockPermissions; + QMap<quint64, std::function<void(const QVariant &)>> m_javaScriptCallbacks; std::unique_ptr<content::DropData> m_currentDropData; uint m_currentDropAction; bool m_updateDragActionCalled; diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index a1ad301ed..4c664fd21 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -147,6 +147,7 @@ public: virtual void titleChanged(const QString&) = 0; virtual void urlChanged() = 0; virtual void iconChanged(const QUrl&) = 0; + virtual void zoomFactorChanged(qreal factor) = 0; virtual void loadProgressChanged(int progress) = 0; virtual void didUpdateTargetURL(const QUrl&) = 0; virtual void selectionChanged() = 0; @@ -175,7 +176,8 @@ public: virtual void javascriptDialog(QSharedPointer<JavaScriptDialogController>) = 0; virtual void runFileChooser(QSharedPointer<FilePickerController>) = 0; virtual void showColorDialog(QSharedPointer<ColorChooserController>) = 0; - virtual void didRunJavaScript(quint64 requestId, const QVariant& result) = 0; + virtual void runJavaScript(const QString &script, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) = 0; virtual void didFetchDocumentMarkup(quint64 requestId, const QString& result) = 0; virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0; virtual void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) = 0; diff --git a/src/pdfquick/qquickpdfpageimage.cpp b/src/pdfquick/qquickpdfpageimage.cpp index f2da067f1..9ff0337a5 100644 --- a/src/pdfquick/qquickpdfpageimage.cpp +++ b/src/pdfquick/qquickpdfpageimage.cpp @@ -42,7 +42,7 @@ QQuickPdfPageImage::~QQuickPdfPageImage() { Q_D(QQuickPdfPageImage); // cancel any async rendering job that is running on my behalf - d->pix.clear(); + d->pendingPix->clear(); } /*! @@ -97,21 +97,23 @@ void QQuickPdfPageImage::load() thisRequestFinished = QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()"); } + static QMetaMethod requestFinishedSlot = staticMetaObject.method(thisRequestFinished); - d->pix.loadImageFromDevice(qmlEngine(this), carrierFile, url, + d->pendingPix->loadImageFromDevice(qmlEngine(this), carrierFile, url, d->sourceClipRect.toRect(), d->sourcesize * d->devicePixelRatio, QQuickImageProviderOptions(), d->currentFrame, d->frameCount); qCDebug(qLcImg) << "loading page" << d->currentFrame << "of" << d->frameCount - << "from" << carrierFile->fileName() << "status" << d->pix.status(); + << "from" << carrierFile->fileName() << "status" << d->pendingPix->status(); - switch (d->pix.status()) { + switch (d->pendingPix->status()) { case QQuickPixmap::Ready: + requestFinishedSlot.invoke(this); pixmapChange(); break; case QQuickPixmap::Loading: - d->pix.connectFinished(this, thisRequestFinished); - d->pix.connectDownloadProgress(this, thisRequestProgress); + d->pendingPix->connectFinished(this, thisRequestFinished); + d->pendingPix->connectDownloadProgress(this, thisRequestProgress); if (d->progress != 0.0) { d->progress = 0.0; emit progressChanged(d->progress); @@ -122,7 +124,7 @@ void QQuickPdfPageImage::load() } break; default: - qCDebug(qLcImg) << "unexpected status" << d->pix.status(); + qCDebug(qLcImg) << "unexpected status" << d->pendingPix->status(); break; } } diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp index 11ed33b5e..7c77f22de 100644 --- a/src/webenginequick/api/qquickwebengineview.cpp +++ b/src/webenginequick/api/qquickwebengineview.cpp @@ -572,6 +572,12 @@ void QQuickWebEngineViewPrivate::iconChanged(const QUrl &url) QTimer::singleShot(0, q, &QQuickWebEngineView::iconChanged); } +void QQuickWebEngineViewPrivate::zoomFactorChanged(qreal factor) +{ + Q_Q(QQuickWebEngineView); + Q_EMIT q->zoomFactorChanged(factor); +} + void QQuickWebEngineViewPrivate::loadProgressChanged(int progress) { Q_Q(QQuickWebEngineView); @@ -1225,7 +1231,6 @@ void QQuickWebEngineView::setZoomFactor(qreal arg) d->adapter->setZoomFactor(arg); // MEMO: should reset if factor was not applied due to being invalid d->m_zoomFactor = zoomFactor(); - emit zoomFactorChanged(d->m_zoomFactor); } else { d->m_zoomFactor = arg; } @@ -1296,19 +1301,18 @@ bool QQuickWebEngineView::activeFocusOnPress() const return d->m_activeFocusOnPress; } -void QQuickWebEngineViewPrivate::didRunJavaScript(quint64 requestId, const QVariant &result) +void QQuickWebEngineViewPrivate::runJavaScript( + const QString &script, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) { - Q_Q(QQuickWebEngineView); - QJSValue callback = m_callbacks.take(requestId); - QJSValueList args; - args.append(qmlEngine(q)->toScriptValue(result)); - callback.call(args); + ensureContentsAdapter(); + adapter->runJavaScript(script, worldId, frameId, callback); } void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { Q_Q(QQuickWebEngineView); - QJSValue callback = m_callbacks.take(requestId); + QJSValue callback = m_printCallbacks.take(requestId); QJSValueList args; args.append(qmlEngine(q)->toScriptValue(*(result.data()))); callback.call(args); @@ -1487,16 +1491,15 @@ void QQuickWebEngineView::runJavaScript(const QString &script, const QJSValue &c void QQuickWebEngineView::runJavaScript(const QString &script, quint32 worldId, const QJSValue &callback) { Q_D(QQuickWebEngineView); - d->ensureContentsAdapter(); + std::function<void(const QVariant &)> wrappedCallback; if (!callback.isUndefined()) { - quint64 requestId = d_ptr->adapter->runJavaScriptCallbackResult(script, worldId); - if (requestId) { - d->m_callbacks.insert(requestId, callback); - } else { - callback.call(); - } - } else - d->adapter->runJavaScript(script, worldId); + wrappedCallback = [this, callback](const QVariant &result) { + QJSValueList args; + args.append(qmlEngine(this)->toScriptValue(result)); + callback.call(args); + }; + } + d->runJavaScript(script, worldId, WebContentsAdapter::kUseMainFrameId, wrappedCallback); } qreal QQuickWebEngineView::zoomFactor() const @@ -1593,7 +1596,7 @@ void QQuickWebEngineView::printToPdf(const QJSValue &callback, PrintedPageSizeId d->ensureContentsAdapter(); quint64 requestId = d->adapter->printToPDFCallbackResult(pageLayout, ranges); - d->m_callbacks.insert(requestId, callback); + d->m_printCallbacks.insert(requestId, callback); #else Q_UNUSED(pageSizeId); Q_UNUSED(orientation); diff --git a/src/webenginequick/api/qquickwebengineview_p_p.h b/src/webenginequick/api/qquickwebengineview_p_p.h index cf4da7c40..50667dda0 100644 --- a/src/webenginequick/api/qquickwebengineview_p_p.h +++ b/src/webenginequick/api/qquickwebengineview_p_p.h @@ -64,6 +64,7 @@ public: void titleChanged(const QString&) override; void urlChanged() override; void iconChanged(const QUrl&) override; + void zoomFactorChanged(qreal factor) override; void loadProgressChanged(int progress) override; void didUpdateTargetURL(const QUrl&) override; void selectionChanged() override; @@ -92,7 +93,8 @@ public: void runFileChooser(QSharedPointer<QtWebEngineCore::FilePickerController>) override; void desktopMediaRequested(QtWebEngineCore::DesktopMediaController *) override; void showColorDialog(QSharedPointer<QtWebEngineCore::ColorChooserController>) override; - void didRunJavaScript(quint64, const QVariant&) override; + void runJavaScript(const QString &script, quint32 worldId, quint64 frameId, + const std::function<void(const QVariant &)> &callback) override; void didFetchDocumentMarkup(quint64, const QString&) override { } void didFetchDocumentInnerText(quint64, const QString&) override { } void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) override; @@ -157,7 +159,7 @@ public: bool m_fullscreenMode; bool isLoading; bool m_activeFocusOnPress; - QMap<quint64, QJSValue> m_callbacks; + QMap<quint64, QJSValue> m_printCallbacks; QQmlWebChannel *m_webChannel; QPointer<QQuickWebEngineView> inspectedView; QPointer<QQuickWebEngineView> devToolsView; diff --git a/src/webenginequick/doc/src/webengineframe.qdoc b/src/webenginequick/doc/src/webengineframe.qdoc index ef2a5c33d..e1c63b923 100644 --- a/src/webenginequick/doc/src/webengineframe.qdoc +++ b/src/webenginequick/doc/src/webengineframe.qdoc @@ -63,3 +63,37 @@ If the frame could not be found, returns a default size with dimensions (-1, -1). */ + +/*! + \qmlmethod void WebEngineFrame::runJavaScript(string script, variant callback) + \qmlmethod void WebEngineFrame::runJavaScript(string script, uint worldId, variant callback) + + Runs the JavaScript code contained in \a script on this frame, without checking + whether the DOM of the page has been constructed. + + To avoid conflicts with other scripts executed on the page, the world in + which the script is run is specified by \a worldId. The world ID values are + the same as provided by QWebEngineScript::ScriptWorldId, and between \c 0 + and \c 256. If you leave out the \c world ID, the script is run in the + \c MainWorld. + + The \a callback parameter is optional. If a callback function is provided, it will be + invoked after the script finishes running. + \code + frame.runJavaScript("document.title", function(result) { console.log(result); }); + \endcode + + Only plain data can be returned from JavaScript as the result value. + Supported data types include all of the JSON data types as well as, for + example, \c{Date} and \c{ArrayBuffer}. Unsupported data types include, for + example, \c{Function} and \c{Promise}. + + The script will run in the same \e world as other scripts that are + part of the loaded site. + + \warning Do not execute lengthy routines in the callback function, because it might block the + rendering of the web content. + + For more information about injecting scripts, see \l {Script Injection}. + For an alternative way to inject scripts, see WebEngineView::userScripts. +*/ diff --git a/src/webenginequick/doc/src/webengineview_lgpl.qdoc b/src/webenginequick/doc/src/webengineview_lgpl.qdoc index dbaea62e4..bbefcd2bc 100644 --- a/src/webenginequick/doc/src/webengineview_lgpl.qdoc +++ b/src/webenginequick/doc/src/webengineview_lgpl.qdoc @@ -367,6 +367,8 @@ /*! \qmlmethod void WebEngineView::runJavaScript(string script, variant callback) + \qmlmethod void WebEngineView::runJavaScript(string script, int worldId, variant callback) + Runs the specified \a script in the content of the web view. The \a callback parameter is optional. If a callback function is provided, @@ -381,8 +383,10 @@ example, \c{Date} and \c{ArrayBuffer}. Unsupported data types include, for example, \c{Function} and \c{Promise}. - The script will run in the same \e world as other scripts that are - part of the loaded site. + To avoid conflicts with other scripts executed on the page, the world in + which the script is run can be specified by \a worldId. The world ID must be + between \c 0 and \c 256. If you leave out the \c world ID, the script is + run in the \c MainWorld. \warning Do not execute lengthy routines in the callback function, because it might block the rendering of the web content. @@ -1598,5 +1602,14 @@ \sa QWebEngineWebAuthUxRequest */ +/*! + \qmlsignal WebEngineView::zoomFactorChanged(qreal factor); + \since QtWebEngine 6.8 + + This signal is emitted whenever the zoom \a factor for the page changes. + + \sa zoomFactor +*/ + \sa {WebEngine Qt Quick Custom Touch Handle Example} */ diff --git a/tests/auto/core/certificateerror/tst_certificateerror.cpp b/tests/auto/core/certificateerror/tst_certificateerror.cpp index 67e2d8ae4..61201e250 100644 --- a/tests/auto/core/certificateerror/tst_certificateerror.cpp +++ b/tests/auto/core/certificateerror/tst_certificateerror.cpp @@ -19,6 +19,7 @@ private Q_SLOTS: void handleError_data(); void handleError(); void fatalError(); + void resourceError(); }; struct PageWithCertificateErrorHandler : QWebEnginePage @@ -130,5 +131,20 @@ void tst_CertificateError::fatalError() } } +void tst_CertificateError::resourceError() +{ + PageWithCertificateErrorHandler page(false, false); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + page.setHtml("<img src=\"https://expired.badssl.com\">"); + if (!page.loadSpy.wait(10000)) { + QVERIFY2(!page.error, "There shouldn't be any certificate error if not loaded due to missing internet access!"); + QSKIP("Couldn't load page from network, skipping test."); + } + + QTRY_VERIFY(page.error); + QCOMPARE(page.error->isMainFrame(), false); +} + QTEST_MAIN(tst_CertificateError) #include <tst_certificateerror.moc> diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp index b70a655d3..7cd075443 100644 --- a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp +++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp @@ -37,6 +37,7 @@ private Q_SLOTS: void childrenOfInvalidFrame(); void url(); void size(); + void runJavaScript(); private: }; @@ -175,6 +176,19 @@ void tst_QWebEngineFrame::size() QCOMPARE(frame1.size(), QSizeF()); } +void tst_QWebEngineFrame::runJavaScript() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto children = page.mainFrame().children(); + CallbackSpy<QVariant> spy; + children[0].runJavaScript("window.name", spy.ref()); + auto result = spy.waitForResult(); + QCOMPARE(result, QString("test-subframe0")); +} + QTEST_MAIN(tst_QWebEngineFrame) #include "tst_qwebengineframe.moc" diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 4d9d02bca..990b1de4f 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -134,6 +134,7 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineCertificateError.defer() --> void" << "QWebEngineCertificateError.description --> QString" << "QWebEngineCertificateError.type --> QWebEngineCertificateError::Type" + << "QWebEngineCertificateError.isMainFrame --> bool" << "QWebEngineCertificateError.acceptCertificate() --> void" << "QWebEngineCertificateError.overridable --> bool" << "QWebEngineCertificateError.rejectCertificate() --> void" @@ -887,6 +888,10 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineFrame.htmlName --> QString" << "QWebEngineFrame.isValid --> bool" << "QWebEngineFrame.name --> QString" + << "QWebEngineFrame.runJavaScript(QString) --> void" + << "QWebEngineFrame.runJavaScript(QString,uint) --> void" + << "QWebEngineFrame.runJavaScript(QString,QJSValue) --> void" + << "QWebEngineFrame.runJavaScript(QString,uint,QJSValue) --> void" << "QWebEngineFrame.size --> QSizeF" << "QWebEngineFrame.url --> QUrl" ; diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 5bd33332b..cebdaaa47 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -205,7 +205,11 @@ void tst_QWebEngineProfile::clearDataFromCache() QCOMPARE_GT(sizeBeforeClear, 0); profile.clearHttpCache(); QTRY_COMPARE(cacheSpy.size(), 1); +#if defined(Q_OS_WIN) + QTRY_COMPARE_GT(sizeBeforeClear, totalSize(cacheDir)); +#else QCOMPARE_GT(sizeBeforeClear, totalSize(cacheDir)); +#endif (void)server.stop(); } diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml index d4bf12e29..0a2a86630 100644 --- a/tests/manual/quick/pdf/withdoc.qml +++ b/tests/manual/quick/pdf/withdoc.qml @@ -19,6 +19,11 @@ Window { onPasswordRequired: function() { passwordDialog.open() } } + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { id: fileDialog title: "Open a PDF file" @@ -85,6 +90,7 @@ Window { PdfPageImage { id: image document: doc + retainWhileLoading: true property real zoomFactor: Math.sqrt(2) |