summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--coin/qt-installer-package-config.json4
-rw-r--r--dependencies.yaml8
-rw-r--r--examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc19
-rw-r--r--examples/webenginewidgets/simplebrowser/tabwidget.cpp5
-rw-r--r--src/core/api/CMakeLists.txt1
-rw-r--r--src/core/api/qwebengineframe.cpp118
-rw-r--r--src/core/api/qwebengineframe.h62
-rw-r--r--src/core/api/qwebenginenavigationrequest.cpp27
-rw-r--r--src/core/api/qwebenginenavigationrequest.h4
-rw-r--r--src/core/api/qwebenginepage.cpp31
-rw-r--r--src/core/api/qwebenginepage.h5
-rw-r--r--src/core/api/qwebenginepage_p.h2
-rw-r--r--src/core/content_browser_client_qt.cpp3
-rw-r--r--src/core/favicon_driver_qt.cpp7
-rw-r--r--src/core/favicon_driver_qt.h3
-rw-r--r--src/core/ozone/gl_context_qt.cpp36
-rw-r--r--src/core/ozone/gl_context_qt.h3
-rw-r--r--src/core/ozone/ozone_platform_qt.cpp5
-rw-r--r--src/core/ozone/surface_factory_qt.cpp2
-rw-r--r--src/core/ozone/surface_factory_qt.h2
-rw-r--r--src/core/web_contents_adapter.cpp74
-rw-r--r--src/core/web_contents_adapter.h14
-rw-r--r--src/core/web_contents_adapter_client.h2
-rw-r--r--src/core/web_contents_delegate_qt.cpp2
-rw-r--r--src/pdf/doc/snippets/pdfpageview.qml12
-rw-r--r--src/pdfquick/PdfPageView.qml2
-rw-r--r--src/pdfwidgets/qpdfview.cpp6
-rw-r--r--src/webenginequick/api/qquickwebenginefaviconprovider.cpp224
-rw-r--r--src/webenginequick/api/qquickwebenginefaviconprovider_p_p.h83
-rw-r--r--src/webenginequick/api/qquickwebengineprofile.cpp4
-rw-r--r--src/webenginequick/api/qquickwebengineprofile.h2
-rw-r--r--src/webenginequick/api/qquickwebengineview.cpp31
-rw-r--r--src/webenginequick/api/qquickwebengineview_p.h9
-rw-r--r--src/webenginequick/api/qquickwebengineview_p_p.h4
-rw-r--r--src/webenginequick/doc/src/webengineframe.qdoc65
-rw-r--r--src/webenginequick/doc/src/webengineview_lgpl.qdoc17
-rw-r--r--tests/auto/core/CMakeLists.txt1
-rw-r--r--tests/auto/core/qwebengineframe/CMakeLists.txt22
-rw-r--r--tests/auto/core/qwebengineframe/resources/frameset.html20
-rw-r--r--tests/auto/core/qwebengineframe/resources/iframes.html17
-rw-r--r--tests/auto/core/qwebengineframe/resources/nesting-iframe.html7
-rw-r--r--tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp180
-rw-r--r--tests/auto/core/qwebengineglobalsettings/CMakeLists.txt1
-rw-r--r--tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp17
-rw-r--r--tests/auto/quick/publicapi/tst_publicapi.cpp10
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp29
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp4
47 files changed, 1001 insertions, 205 deletions
diff --git a/coin/qt-installer-package-config.json b/coin/qt-installer-package-config.json
index ac604d77b..aec45688b 100644
--- a/coin/qt-installer-package-config.json
+++ b/coin/qt-installer-package-config.json
@@ -18,7 +18,9 @@
"**/plugins/imageformats/**/*",
"**/qml/QtQuick/**/*",
"**/qml/QtQuick/Pdf/**/.rcc/*",
- "**/qml/QtQuick/Pdf/**/.rcc/qmlcache/*"
+ "**/qml/QtQuick/Pdf/**/.rcc/qmlcache/*",
+ "**/qml/QtQuick/Pdf/**/.qt/rcc/*",
+ "**/qml/QtQuick/Pdf/**/.qt/rcc/qmlcache/*"
]
}
}
diff --git a/dependencies.yaml b/dependencies.yaml
index 1af963255..0c1d011bf 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,13 +1,13 @@
dependencies:
../qtdeclarative:
- ref: 330fa93d6e9003c0ea188b9e703f2b3f0448f8c8
+ ref: c63bb2bad5b4e741ed8a1e16d8f1f916c9baf61d
required: true
../qtpositioning:
- ref: 21bbe4ee94c3a81b91610c2cc374415fdfdd3380
+ ref: 152d0039c7ad2898e8e5b122420f59049536143b
required: false
../qttools:
- ref: 70f1c85c0a60ac80f088abdff08edfa902cca38f
+ ref: 0d80d76bf14905204a248655cd88fe6cfd5706db
required: false
../qtwebchannel:
- ref: 733ab7219390133a0cd6a22bfb6ff35eee15c6dd
+ ref: 2a194a801a86d3d9342b47ccba9a1105aadf70d6
required: false
diff --git a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
index dd7a8b998..211535204 100644
--- a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
+++ b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
@@ -103,6 +103,25 @@
\skipto TabWidget::setupView
\printuntil /^\}/
+ \section1 Closing Tabs
+
+ When the user closes a tab, we first trigger the \l {QWebEnginePage::}{RequestClose} web action
+ on the corresponding \c WebView:
+
+ \quotefromfile webenginewidgets/simplebrowser/tabwidget.cpp
+ \skipto QTabBar::tabCloseRequested
+ \printuntil }
+
+ This allows any JavaScript \c beforeunload event listeners to fire, which may
+ prompt the user with a dialog to confirm that they want to close the page.
+ In this case, the user can reject the close request and leave the tab open,
+ otherwise the \l {QWebEnginePage::}{windowCloseRequested} signal is emitted and we close the
+ tab:
+
+ \quotefromfile webenginewidgets/simplebrowser/tabwidget.cpp
+ \skipto QWebEnginePage::windowCloseRequested
+ \printuntil }
+
\section1 Implementing WebView Functionality
The \c WebView is derived from QWebEngineView to support the following
diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.cpp b/examples/webenginewidgets/simplebrowser/tabwidget.cpp
index f9037a8e1..acdf49510 100644
--- a/examples/webenginewidgets/simplebrowser/tabwidget.cpp
+++ b/examples/webenginewidgets/simplebrowser/tabwidget.cpp
@@ -21,7 +21,10 @@ TabWidget::TabWidget(QWebEngineProfile *profile, QWidget *parent)
tabBar->setMovable(true);
tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tabBar, &QTabBar::customContextMenuRequested, this, &TabWidget::handleContextMenuRequested);
- connect(tabBar, &QTabBar::tabCloseRequested, this, &TabWidget::closeTab);
+ connect(tabBar, &QTabBar::tabCloseRequested, [this](int index) {
+ if (WebView *view = webView(index))
+ view->page()->triggerAction(QWebEnginePage::WebAction::RequestClose);
+ });
connect(tabBar, &QTabBar::tabBarDoubleClicked, [this](int index) {
if (index == -1)
createTab();
diff --git a/src/core/api/CMakeLists.txt b/src/core/api/CMakeLists.txt
index f2ceb2dfd..c06f5e4ce 100644
--- a/src/core/api/CMakeLists.txt
+++ b/src/core/api/CMakeLists.txt
@@ -19,6 +19,7 @@ qt_internal_add_module(WebEngineCore
qwebenginedownloadrequest.cpp qwebenginedownloadrequest.h qwebenginedownloadrequest_p.h
qwebenginefilesystemaccessrequest.cpp qwebenginefilesystemaccessrequest.h
qwebenginefindtextresult.cpp qwebenginefindtextresult.h
+ qwebengineframe.cpp qwebengineframe.h
qwebenginefullscreenrequest.cpp qwebenginefullscreenrequest.h
qwebenginehistory.cpp qwebenginehistory.h qwebenginehistory_p.h
qwebenginehttprequest.cpp qwebenginehttprequest.h
diff --git a/src/core/api/qwebengineframe.cpp b/src/core/api/qwebengineframe.cpp
new file mode 100644
index 000000000..edd89d663
--- /dev/null
+++ b/src/core/api/qwebengineframe.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwebengineframe.h"
+
+#include "web_contents_adapter_client.h"
+#include "web_contents_adapter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWebEngineFrame
+ \brief The QWebEngineFrame class gives information about and control over a page frame.
+ \since 6.8
+
+ \inmodule QtWebEngineCore
+
+ A web engine frame represents a single frame within a web page, such as those created by
+ \c <frame> or \c <iframe> HTML elements.
+ An active QWebEnginePage has one or more frames arranged in a tree structure. The top-level
+ frame, the root of this tree, can be accessed through the mainFrame() method, and
+ children() provides a frame's direct descendants.
+
+ A frame's lifetime is, at most, as long as the QWebEnginePage object that produced it.
+ However, frames may be created and deleted spontaneously and dynamically, for example through
+ navigation and script execution. Because of this, many QWebEngineFrame methods return
+ optional values, which will be \c std::nullopt if the frame no longer exists.
+*/
+
+/*! \internal
+ */
+QWebEngineFrame::QWebEngineFrame(QtWebEngineCore::WebContentsAdapterClient *adapter, quint64 id)
+ : m_adapterClient(adapter), m_id(id)
+{
+}
+
+/*!
+ Returns \c{true} if this object represents an existing frame; \c{false} otherwise.
+
+ Once a frame is invalid, it never becomes valid again.
+*/
+bool QWebEngineFrame::isValid() const
+{
+ return m_adapterClient->webContentsAdapter()->hasFrame(m_id);
+}
+
+/*!
+ Returns the frame name; that is, what would be returned by \c window.name in JavaScript.
+
+ If the frame could not be found, returns a null QString.
+
+ \sa htmlName
+*/
+QString QWebEngineFrame::name() const
+{
+ return m_adapterClient->webContentsAdapter()->frameName(m_id);
+}
+
+/*!
+ Returns the value of the frame's \c name HTML attribute, or an empty string if it has none.
+
+ If the frame could not be found, returns a null QString.
+
+ \sa name
+*/
+QString QWebEngineFrame::htmlName() const
+{
+ return m_adapterClient->webContentsAdapter()->frameHtmlName(m_id);
+}
+
+/*!
+ Returns a list of the frame's children in an arbitrary order.
+
+ If the frame could not be found, returns an empty list.
+ */
+QList<QWebEngineFrame> QWebEngineFrame::children() const
+{
+ QList<QWebEngineFrame> result;
+ for (auto childId : m_adapterClient->webContentsAdapter()->frameChildren(m_id))
+ result.push_back(QWebEngineFrame{ m_adapterClient, childId });
+ return result;
+}
+
+/*!
+ Returns the URL of the content currently loaded in this frame.
+
+ If the frame could not be found, returns an empty QUrl.
+ */
+QUrl QWebEngineFrame::url() const
+{
+ return m_adapterClient->webContentsAdapter()->frameUrl(m_id);
+}
+
+/*!
+ Returns the size of the frame within the viewport.
+
+ If the frame could not be found, returns QSizeF().
+ */
+QSizeF QWebEngineFrame::size() const
+{
+ return m_adapterClient->webContentsAdapter()->frameSize(m_id);
+}
+
+/*! \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,
+ otherwise \c{false}.
+ */
+
+/*! \fn bool QWebEngineFrame::operator!=(const QWebEngineFrame &left, const QWebEngineFrame &right) noexcept
+
+ Returns \c{true} if \a left and \a right represent different frames in the same web page,
+ otherwise \c{false}.
+ */
+
+QT_END_NAMESPACE
+
+#include "moc_qwebengineframe.cpp"
diff --git a/src/core/api/qwebengineframe.h b/src/core/api/qwebengineframe.h
new file mode 100644
index 000000000..e58961848
--- /dev/null
+++ b/src/core/api/qwebengineframe.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWEBENGINEFRAME_H
+#define QWEBENGINEFRAME_H
+
+#include <QtWebEngineCore/qtwebenginecoreglobal.h>
+#include <QtQml/qqmlregistration.h>
+#include <QtCore/qcompare.h>
+#include <QtCore/QList>
+#include <QtCore/QSizeF>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+
+namespace QtWebEngineCore {
+class WebContentsAdapterClient;
+}
+
+QT_BEGIN_NAMESPACE
+
+class Q_WEBENGINECORE_EXPORT QWebEngineFrame
+{
+ Q_GADGET
+
+ Q_PROPERTY(bool isValid READ isValid FINAL)
+ Q_PROPERTY(QString name READ name FINAL)
+ Q_PROPERTY(QString htmlName READ htmlName FINAL)
+ Q_PROPERTY(QUrl url READ url FINAL)
+ Q_PROPERTY(QSizeF size READ size FINAL)
+
+public:
+ QML_VALUE_TYPE(webEngineFrame)
+ QML_ADDED_IN_VERSION(6, 8)
+
+ bool isValid() const;
+ QString name() const;
+ QString htmlName() const;
+ QList<QWebEngineFrame> children() const;
+ QUrl url() const;
+ QSizeF size() const;
+
+ friend inline bool comparesEqual(const QWebEngineFrame &lhs,
+ const QWebEngineFrame &rhs) noexcept
+ {
+ return lhs.m_adapterClient == rhs.m_adapterClient && lhs.m_id == rhs.m_id;
+ }
+
+ Q_DECLARE_EQUALITY_COMPARABLE(QWebEngineFrame);
+
+private:
+ friend class QWebEnginePage;
+ friend class QQuickWebEngineView;
+
+ QWebEngineFrame(QtWebEngineCore::WebContentsAdapterClient *page, quint64 id);
+
+ QtWebEngineCore::WebContentsAdapterClient *m_adapterClient;
+ quint64 m_id;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBENGINEFRAME_H
diff --git a/src/core/api/qwebenginenavigationrequest.cpp b/src/core/api/qwebenginenavigationrequest.cpp
index 0a30f6472..dc7447b88 100644
--- a/src/core/api/qwebenginenavigationrequest.cpp
+++ b/src/core/api/qwebenginenavigationrequest.cpp
@@ -9,15 +9,17 @@ QT_BEGIN_NAMESPACE
class QWebEngineNavigationRequestPrivate {
public:
- QWebEngineNavigationRequestPrivate(const QUrl& url, QWebEngineNavigationRequest::NavigationType navigationType, bool mainFrame)
+ QWebEngineNavigationRequestPrivate(const QUrl& url, QWebEngineNavigationRequest::NavigationType navigationType, bool mainFrame, bool formData)
: url(url)
, navigationType(navigationType)
, isMainFrame(mainFrame)
+ , hasFormData(formData)
{}
QUrl url;
QWebEngineNavigationRequest::NavigationType navigationType;
bool isMainFrame;
+ bool hasFormData;
bool isAccepted = true;
};
@@ -51,9 +53,9 @@ public:
/*! \internal
*/
-QWebEngineNavigationRequest::QWebEngineNavigationRequest(const QUrl& url, QWebEngineNavigationRequest::NavigationType navigationType, bool mainFrame, QObject* parent)
+QWebEngineNavigationRequest::QWebEngineNavigationRequest(const QUrl& url, QWebEngineNavigationRequest::NavigationType navigationType, bool mainFrame, bool formData, QObject* parent)
: QObject(parent)
- , d_ptr(new QWebEngineNavigationRequestPrivate(url, navigationType, mainFrame))
+ , d_ptr(new QWebEngineNavigationRequestPrivate(url, navigationType, mainFrame, formData))
{
}
@@ -174,6 +176,25 @@ bool QWebEngineNavigationRequest::isMainFrame() const
return d->isMainFrame;
}
+/*!
+ \property QWebEngineNavigationRequest::hasFormData
+ \brief Whether the navigation request contains form data
+ \since 6.8
+*/
+/*!
+ \qmlproperty bool WebEngineNavigationRequest::hasFormData
+ \since 6.8
+ \readonly
+
+ Whether the navigation request contains form data
+*/
+
+bool QWebEngineNavigationRequest::hasFormData() const
+{
+ Q_D(const QWebEngineNavigationRequest);
+ return d->hasFormData;
+}
+
/*! \internal */
bool QWebEngineNavigationRequest::isAccepted() const
{
diff --git a/src/core/api/qwebenginenavigationrequest.h b/src/core/api/qwebenginenavigationrequest.h
index 12fc2b4a1..a810a59fe 100644
--- a/src/core/api/qwebenginenavigationrequest.h
+++ b/src/core/api/qwebenginenavigationrequest.h
@@ -17,6 +17,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineNavigationRequest : public QObject
Q_OBJECT
Q_PROPERTY(QUrl url READ url CONSTANT FINAL)
Q_PROPERTY(bool isMainFrame READ isMainFrame CONSTANT FINAL)
+ Q_PROPERTY(bool hasFormData READ hasFormData CONSTANT REVISION(6, 8) FINAL)
Q_PROPERTY(NavigationType navigationType READ navigationType CONSTANT FINAL)
public:
@@ -36,6 +37,7 @@ public:
QUrl url() const;
bool isMainFrame() const;
+ bool hasFormData() const;
NavigationType navigationType() const;
Q_INVOKABLE void accept();
@@ -60,7 +62,7 @@ Q_SIGNALS:
#endif
private:
- QWebEngineNavigationRequest(const QUrl &url, NavigationType navigationType, bool mainFrame,
+ QWebEngineNavigationRequest(const QUrl &url, NavigationType navigationType, bool mainFrame, bool formData,
QObject *parent = nullptr);
friend class QWebEnginePagePrivate;
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp
index ac645c430..c2a737d72 100644
--- a/src/core/api/qwebenginepage.cpp
+++ b/src/core/api/qwebenginepage.cpp
@@ -1516,13 +1516,13 @@ void QWebEnginePagePrivate::contextMenuRequested(QWebEngineContextMenuRequest *d
\sa acceptNavigationRequest()
*/
-void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame)
+void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame, bool hasFormData)
{
Q_Q(QWebEnginePage);
accepted = q->acceptNavigationRequest(url, static_cast<QWebEnginePage::NavigationType>(navigationType), isMainFrame);
if (accepted) {
- QWebEngineNavigationRequest navigationRequest(url, static_cast<QWebEngineNavigationRequest::NavigationType>(navigationType), isMainFrame);
+ QWebEngineNavigationRequest navigationRequest(url, static_cast<QWebEngineNavigationRequest::NavigationType>(navigationType), isMainFrame, hasFormData);
Q_EMIT q->navigationRequested(navigationRequest);
accepted = navigationRequest.isAccepted();
}
@@ -2501,6 +2501,33 @@ void QWebEnginePage::setVisible(bool visible)
d->adapter->setVisible(visible);
}
+/*!
+ \since 6.8
+
+ The main, top-level frame of the page. All other frames on this page are accessible
+ as children of the main frame.
+*/
+QWebEngineFrame QWebEnginePage::mainFrame()
+{
+ Q_D(QWebEnginePage);
+ return QWebEngineFrame(d, d->adapter->mainFrameId());
+}
+
+/*!
+ \since 6.8
+
+ Returns the frame with the given \a name. If there are multiple frames with the same
+ name, which one is returned is arbitrary. If no frame was found, returns \c std::nullopt.
+*/
+std::optional<QWebEngineFrame> QWebEnginePage::findFrameByName(const QString &name)
+{
+ Q_D(QWebEnginePage);
+ if (auto maybeId = d->adapter->findFrameIdByName(name)) {
+ return QWebEngineFrame(d, *maybeId);
+ }
+ return {};
+}
+
QDataStream &operator<<(QDataStream &stream, const QWebEngineHistory &history)
{
auto adapter = history.d_func()->adapter();
diff --git a/src/core/api/qwebenginepage.h b/src/core/api/qwebenginepage.h
index c857585ab..e5a4e9551 100644
--- a/src/core/api/qwebenginepage.h
+++ b/src/core/api/qwebenginepage.h
@@ -8,6 +8,7 @@
#include <QtWebEngineCore/qwebengineclientcertificateselection.h>
#include <QtWebEngineCore/qwebenginedownloadrequest.h>
#include <QtWebEngineCore/qwebenginequotarequest.h>
+#include <QtWebEngineCore/qwebengineframe.h>
#include <QtCore/qobject.h>
#include <QtCore/qurl.h>
@@ -16,6 +17,7 @@
#include <QtGui/qtgui-config.h>
#include <functional>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -301,6 +303,9 @@ public:
bool isVisible() const;
void setVisible(bool visible);
+ QWebEngineFrame mainFrame();
+ std::optional<QWebEngineFrame> findFrameByName(const QString &name);
+
void acceptAsNewWindow(QWebEngineNewWindowRequest &request);
Q_SIGNALS:
diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h
index a51d8603b..31ace7e9d 100644
--- a/src/core/api/qwebenginepage_p.h
+++ b/src/core/api/qwebenginepage_p.h
@@ -124,7 +124,7 @@ public:
void windowCloseRejected() override;
void desktopMediaRequested(QtWebEngineCore::DesktopMediaController *) override;
void contextMenuRequested(QWebEngineContextMenuRequest *request) override;
- void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame) override;
+ void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame, bool hasFormData) override;
void requestFullScreenMode(const QUrl &origin, bool fullscreen) override;
bool isFullScreenMode() const override;
void javascriptDialog(QSharedPointer<QtWebEngineCore::JavaScriptDialogController>) override;
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 53c2caa2d..7192edbc2 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -827,7 +827,8 @@ static bool navigationThrottleCallback(content::NavigationHandle *handle)
client->navigationRequested(pageTransitionToNavigationType(transition_type),
toQt(handle->GetURL()),
navigationAccepted,
- handle->IsInPrimaryMainFrame());
+ handle->IsInPrimaryMainFrame(),
+ handle->IsFormSubmission());
return !navigationAccepted;
}
diff --git a/src/core/favicon_driver_qt.cpp b/src/core/favicon_driver_qt.cpp
index bb4a734b4..e710d5358 100644
--- a/src/core/favicon_driver_qt.cpp
+++ b/src/core/favicon_driver_qt.cpp
@@ -263,13 +263,6 @@ void FaviconDriverQt::DidUpdateFaviconURL(
if (!entry)
return;
- // We update |m_faviconUrls| even if the list is believed to be partial
- // (checked below), because callers of our getter favicon_urls() expect so.
- std::vector<blink::mojom::FaviconURLPtr> faviconUrls;
- for (const auto &candidate : candidates)
- faviconUrls.push_back(candidate.Clone());
- m_faviconUrls = std::move(faviconUrls);
-
if (!rfh->IsDocumentOnLoadCompletedInMainFrame())
return;
diff --git a/src/core/favicon_driver_qt.h b/src/core/favicon_driver_qt.h
index 96bd682a2..6dc377969 100644
--- a/src/core/favicon_driver_qt.h
+++ b/src/core/favicon_driver_qt.h
@@ -137,9 +137,6 @@ private:
GURL m_bypassCachePageURL;
- // nullopt until the actual list is reported via DidUpdateFaviconURL().
- absl::optional<std::vector<blink::mojom::FaviconURLPtr>> m_faviconUrls;
-
int m_completedHandlersCount = 0;
FaviconStatusQt m_latestFavicon;
diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp
index 0042f2bce..2e358c0fb 100644
--- a/src/core/ozone/gl_context_qt.cpp
+++ b/src/core/ozone/gl_context_qt.cpp
@@ -179,7 +179,7 @@ bool GLContextHelper::isCreateContextRobustnessSupported()
class ScopedGLContext
{
public:
- ScopedGLContext()
+ ScopedGLContext(QOffscreenSurface *surface)
: m_context(new QOpenGLContext())
, m_previousContext(gl::GLContext::GetCurrent())
, m_previousSurface(gl::GLSurface::GetCurrent())
@@ -189,10 +189,7 @@ public:
return;
}
- QOffscreenSurface *surface = new QOffscreenSurface(m_context->screen(), m_context.get());
- surface->create();
Q_ASSERT(surface->isValid());
-
if (!m_context->makeCurrent(surface)) {
qWarning("Failed to make OpenGL context current.");
return;
@@ -269,7 +266,8 @@ EGLHelper *EGLHelper::instance()
return &eglHelper;
}
-EGLHelper::EGLHelper() : m_functions(new EGLHelper::EGLFunctions())
+EGLHelper::EGLHelper()
+ : m_functions(new EGLHelper::EGLFunctions()), m_offscreenSurface(new QOffscreenSurface())
{
const char *extensions = m_functions->eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (!extensions) {
@@ -288,6 +286,9 @@ EGLHelper::EGLHelper() : m_functions(new EGLHelper::EGLFunctions())
return;
}
+ Q_ASSERT(QThread::currentThread() == qApp->thread());
+ m_offscreenSurface->create();
+
const char *displayExtensions = m_functions->eglQueryString(eglDisplay, EGL_EXTENSIONS);
m_isDmaBufSupported = strstr(displayExtensions, "EGL_EXT_image_dma_buf_import")
&& strstr(displayExtensions, "EGL_EXT_image_dma_buf_import_modifiers")
@@ -306,6 +307,16 @@ EGLHelper::EGLHelper() : m_functions(new EGLHelper::EGLFunctions())
const char *displayVendor = m_functions->eglQueryString(eglDisplay, EGL_VENDOR);
m_isDmaBufSupported = !strstr(displayVendor, "NVIDIA");
}
+
+ // Try to create dma-buf.
+ if (m_isDmaBufSupported) {
+ int fd = -1;
+ queryDmaBuf(2, 2, &fd, nullptr, nullptr, nullptr);
+ if (fd == -1)
+ m_isDmaBufSupported = false;
+ else
+ close(fd);
+ }
}
void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *stride, int *offset,
@@ -314,7 +325,7 @@ void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *str
if (!m_isDmaBufSupported)
return;
- ScopedGLContext context;
+ ScopedGLContext context(m_offscreenSurface.get());
if (!context.isValid())
return;
@@ -350,18 +361,7 @@ void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *str
bool EGLHelper::isDmaBufSupported()
{
- if (!m_isDmaBufSupported)
- return false;
-
- int fd = -1;
- queryDmaBuf(2, 2, &fd, nullptr, nullptr, nullptr);
- if (fd == -1) {
- m_isDmaBufSupported = false;
- return false;
- }
-
- close(fd);
- return true;
+ return m_isDmaBufSupported;
}
#endif // QT_CONFIG(opengl) && defined(USE_OZONE)
diff --git a/src/core/ozone/gl_context_qt.h b/src/core/ozone/gl_context_qt.h
index bd1137053..41c6a5f0c 100644
--- a/src/core/ozone/gl_context_qt.h
+++ b/src/core/ozone/gl_context_qt.h
@@ -21,6 +21,8 @@ class GLSurface;
QT_BEGIN_NAMESPACE
+class QOffscreenSurface;
+
class GLContextHelper : public QObject {
Q_OBJECT
public:
@@ -80,6 +82,7 @@ private:
EGLHelper();
QScopedPointer<EGLFunctions> m_functions;
+ QScopedPointer<QOffscreenSurface> m_offscreenSurface;
bool m_isDmaBufSupported = false;
};
#endif // QT_CONFIG(opengl) && defined(USE_OZONE)
diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp
index e8547fa87..4546a2b6a 100644
--- a/src/core/ozone/ozone_platform_qt.cpp
+++ b/src/core/ozone/ozone_platform_qt.cpp
@@ -73,7 +73,8 @@ public:
if (has_initialized_gpu()) {
// This property is set when the GetPlatformRuntimeProperties is
// called on the gpu process side.
- properties.supports_native_pixmaps = surface_factory_ozone_->SupportsNativePixmaps();
+ DCHECK(m_supportsNativePixmaps);
+ properties.supports_native_pixmaps = m_supportsNativePixmaps.value();
}
return properties;
}
@@ -85,6 +86,7 @@ private:
void InitScreen(ui::PlatformScreen *) override {}
+ absl::optional<bool> m_supportsNativePixmaps;
std::unique_ptr<QtWebEngineCore::SurfaceFactoryQt> surface_factory_ozone_;
std::unique_ptr<CursorFactory> cursor_factory_;
@@ -227,6 +229,7 @@ bool OzonePlatformQt::InitializeUI(const ui::OzonePlatform::InitParams &)
input_controller_ = CreateStubInputController();
cursor_factory_.reset(new BitmapCursorFactory());
gpu_platform_support_host_.reset(ui::CreateStubGpuPlatformSupportHost());
+ m_supportsNativePixmaps = QtWebEngineCore::SurfaceFactoryQt::SupportsNativePixmaps();
return true;
}
diff --git a/src/core/ozone/surface_factory_qt.cpp b/src/core/ozone/surface_factory_qt.cpp
index 204f4d62d..4a2006a2d 100644
--- a/src/core/ozone/surface_factory_qt.cpp
+++ b/src/core/ozone/surface_factory_qt.cpp
@@ -241,7 +241,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle(
#endif // QT_CONFIG(opengl)
}
-bool SurfaceFactoryQt::SupportsNativePixmaps() const
+bool SurfaceFactoryQt::SupportsNativePixmaps()
{
#if QT_CONFIG(opengl)
#if BUILDFLAG(OZONE_PLATFORM_X11)
diff --git a/src/core/ozone/surface_factory_qt.h b/src/core/ozone/surface_factory_qt.h
index 07d7337ac..d69467a26 100644
--- a/src/core/ozone/surface_factory_qt.h
+++ b/src/core/ozone/surface_factory_qt.h
@@ -40,7 +40,7 @@ public:
gfx::BufferFormat format,
gfx::NativePixmapHandle handle) override;
- bool SupportsNativePixmaps() const;
+ static bool SupportsNativePixmaps();
private:
std::vector<gl::GLImplementationParts> m_impl;
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 7ffeaaa5b..6decf8780 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1812,6 +1812,80 @@ void WebContentsAdapter::changeTextDirection(bool leftToRight)
}
}
+quint64 WebContentsAdapter::mainFrameId() const
+{
+ CHECK_INITIALIZED(content::RenderFrameHost::kNoFrameTreeNodeId);
+ return static_cast<quint64>(m_webContents->GetPrimaryMainFrame()->GetFrameTreeNodeId());
+}
+
+#define CHECK_INITIALIZED_AND_VALID_FRAME(webengine_frame_id_variable, frame_tree_node_variable, \
+ return_value) \
+ CHECK_INITIALIZED(return_value); \
+ if (webengine_frame_id_variable == kInvalidFrameId) \
+ return return_value; \
+ auto *frame_tree_node_variable = content::FrameTreeNode::GloballyFindByID( \
+ static_cast<int>(webengine_frame_id_variable)); \
+ if (!frame_tree_node_variable) \
+ return return_value
+
+QString WebContentsAdapter::frameName(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, QString());
+ return QString::fromStdString(ftn->frame_name());
+}
+
+QString WebContentsAdapter::frameHtmlName(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, QString());
+ auto &maybeName = ftn->html_name();
+ return maybeName ? QString::fromStdString(*maybeName) : QString("");
+}
+
+QList<quint64> WebContentsAdapter::frameChildren(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, {});
+ QList<quint64> result;
+ size_t numChildren = ftn->child_count();
+ result.reserve(numChildren);
+ for (size_t i = 0; i < numChildren; ++i) {
+ result.push_back(ftn->child_at(i)->frame_tree_node_id());
+ }
+ return result;
+}
+
+QUrl WebContentsAdapter::frameUrl(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, QUrl());
+ return toQt(ftn->current_url());
+}
+
+QSizeF WebContentsAdapter::frameSize(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, QSizeF());
+ auto *rfh = ftn->current_frame_host();
+ Q_ASSERT(rfh);
+ auto maybeSize = rfh->GetFrameSize();
+ return maybeSize ? toQt(*maybeSize) : QSizeF();
+}
+
+std::optional<quint64> WebContentsAdapter::findFrameIdByName(const QString &name) const
+{
+ CHECK_INITIALIZED({});
+ auto *ftn = content::FrameTreeNode::From(m_webContents->GetPrimaryMainFrame());
+ Q_ASSERT(ftn);
+ if (auto *foundFtn = ftn->frame_tree().FindByName(name.toStdString()))
+ return foundFtn->frame_tree_node_id();
+ return {};
+}
+
+bool WebContentsAdapter::hasFrame(quint64 id) const
+{
+ CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, false);
+ auto *rfh = ftn->current_frame_host();
+ Q_ASSERT(rfh);
+ return content::WebContents::FromRenderFrameHost(rfh) == m_webContents.get();
+}
+
WebContentsAdapterClient::RenderProcessTerminationStatus
WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) {
auto status = WebContentsAdapterClient::RenderProcessTerminationStatus(-1);
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 62c3f087c..a5cad8664 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -23,10 +23,12 @@
#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
#include <QtWebEngineCore/qwebenginecontextmenurequest.h>
#include <QtWebEngineCore/qwebenginehttprequest.h>
+#include <QtWebEngineCore/qwebengineframe.h>
#include "web_contents_adapter_client.h"
#include <memory>
+#include <optional>
namespace blink {
namespace web_pref {
@@ -62,6 +64,9 @@ class WebChannelIPCTransportHost;
class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<WebContentsAdapter> {
public:
+ // Sentinel to indicate a frame doesn't exist, for example with `findFrameByName`
+ static constexpr quint64 kInvalidFrameId = -3;
+
static QSharedPointer<WebContentsAdapter> createFromSerializedNavigationHistory(QDataStream &input, WebContentsAdapterClient *adapterClient);
WebContentsAdapter();
WebContentsAdapter(std::unique_ptr<content::WebContents> webContents);
@@ -204,6 +209,15 @@ public:
void resetTouchSelectionController();
void changeTextDirection(bool leftToRight);
+ quint64 mainFrameId() const;
+ QString frameName(quint64 id) const;
+ QString frameHtmlName(quint64 id) const;
+ QList<quint64> frameChildren(quint64 id) const;
+ QUrl frameUrl(quint64 id) const;
+ QSizeF frameSize(quint64 id) const;
+ std::optional<quint64> findFrameIdByName(const QString &name) const;
+ bool hasFrame(quint64 id) const;
+
// meant to be used within WebEngineCore only
void initialize(content::SiteInstance *site);
content::WebContents *webContents() const;
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 1a1474644..a1ad301ed 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -169,7 +169,7 @@ public:
virtual void windowCloseRejected() = 0;
virtual void contextMenuRequested(QWebEngineContextMenuRequest *request) = 0;
virtual void desktopMediaRequested(DesktopMediaController *) = 0;
- virtual void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame) = 0;
+ virtual void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame, bool hasFormData) = 0;
virtual void requestFullScreenMode(const QUrl &origin, bool fullscreen) = 0;
virtual bool isFullScreenMode() const = 0;
virtual void javascriptDialog(QSharedPointer<JavaScriptDialogController>) = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 194274fcb..4df73fb69 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -760,7 +760,7 @@ void WebContentsDelegateQt::launchExternalURL(const QUrl &url, ui::PageTransitio
}
if (navigationAllowedByPolicy) {
- m_viewClient->navigationRequested(pageTransitionToNavigationType(page_transition), url, navigationRequestAccepted, is_main_frame);
+ m_viewClient->navigationRequested(pageTransitionToNavigationType(page_transition), url, navigationRequestAccepted, is_main_frame, false);
#if QT_CONFIG(desktopservices)
if (navigationRequestAccepted)
QDesktopServices::openUrl(url);
diff --git a/src/pdf/doc/snippets/pdfpageview.qml b/src/pdf/doc/snippets/pdfpageview.qml
new file mode 100644
index 000000000..5e233961a
--- /dev/null
+++ b/src/pdf/doc/snippets/pdfpageview.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+import QtQuick
+import QtQuick.Pdf
+
+PdfPageView {
+ document: PdfDocument { source: "my.pdf" }
+}
+//! [0]
+
diff --git a/src/pdfquick/PdfPageView.qml b/src/pdfquick/PdfPageView.qml
index 290570f2b..e1d97f57b 100644
--- a/src/pdfquick/PdfPageView.qml
+++ b/src/pdfquick/PdfPageView.qml
@@ -30,7 +30,7 @@ Rectangle {
A PdfDocument object with a valid \c source URL is required:
- \snippet multipageview.qml 0
+ \snippet pdfpageview.qml 0
*/
required property PdfDocument document
diff --git a/src/pdfwidgets/qpdfview.cpp b/src/pdfwidgets/qpdfview.cpp
index a67667fed..a19b77a7f 100644
--- a/src/pdfwidgets/qpdfview.cpp
+++ b/src/pdfwidgets/qpdfview.cpp
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcLink, "qt.pdf.links")
+Q_LOGGING_CATEGORY(qLcWLink, "qt.pdf.widgets.links")
//#define DEBUG_LINKS
static const QColor SearchResultHighlight("#80B0C4DE");
@@ -681,7 +681,7 @@ void QPdfView::mouseMoveEvent(QMouseEvent *event)
auto dest = d->m_linkModel.linkAt(posInPoints);
setCursor(dest.isValid() ? Qt::PointingHandCursor : Qt::ArrowCursor);
if (dest.isValid())
- qCDebug(qLcLink) << event->position() << ":" << posInPoints << "pt ->" << dest;
+ qCDebug(qLcWLink) << event->position() << ":" << posInPoints << "pt ->" << dest;
}
}
}
@@ -699,7 +699,7 @@ void QPdfView::mouseReleaseEvent(QMouseEvent *event)
d->m_linkModel.setPage(page);
auto dest = d->m_linkModel.linkAt(posInPoints);
if (dest.isValid()) {
- qCDebug(qLcLink) << event << ": jumping to" << dest;
+ qCDebug(qLcWLink) << event << ": jumping to" << dest;
d->m_pageNavigator->jump(dest.page(), dest.location(), dest.zoom());
// TODO scroll and zoom to where the link tells us to
}
diff --git a/src/webenginequick/api/qquickwebenginefaviconprovider.cpp b/src/webenginequick/api/qquickwebenginefaviconprovider.cpp
index 56bbb97ac..00c7f1949 100644
--- a/src/webenginequick/api/qquickwebenginefaviconprovider.cpp
+++ b/src/webenginequick/api/qquickwebenginefaviconprovider.cpp
@@ -11,9 +11,9 @@
#include "web_contents_adapter.h"
#include <QtCore/qmimedatabase.h>
-#include <QtCore/qtimer.h>
#include <QtGui/qicon.h>
#include <QtGui/qpixmap.h>
+#include <QThread>
QT_BEGIN_NAMESPACE
@@ -79,123 +79,167 @@ static bool isIconURL(const QUrl &url)
return false;
}
-static QQuickWebEngineView *findViewById(const QString &id, QList<QQuickWebEngineView *> *views)
-{
- QQuickWebEngineView *result = nullptr;
- for (QQuickWebEngineView *view : *views) {
- if (isIconURL(QUrl(id))) {
- if (view->icon() == QQuickWebEngineFaviconProvider::faviconProviderUrl(QUrl(id))) {
- result = view;
- break;
- }
- } else if (view->url() == QUrl(id)) {
- result = view;
- break;
- }
- }
-
- return result;
-}
-
-FaviconImageResponseRunnable::FaviconImageResponseRunnable(const QString &id,
- const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views)
- : m_id(id), m_requestedSize(requestedSize), m_views(views)
+FaviconImageRequester::FaviconImageRequester(const QUrl &imageSource, const QSize &requestedSize)
+ : m_imageSource(imageSource), m_requestedSize(requestedSize)
{
}
-void FaviconImageResponseRunnable::run()
+void FaviconImageRequester::start()
{
- if (tryNextView() == -1) {
+ if (!tryNextView()) {
// There is no non-otr view to access icon database.
Q_EMIT done(QPixmap());
}
}
-void FaviconImageResponseRunnable::iconRequestDone(const QIcon &icon)
+void FaviconImageRequester::iconRequestDone(const QIcon &icon)
{
if (icon.isNull()) {
- if (tryNextView() == -1) {
+ if (!tryNextView()) {
// Ran out of views.
Q_EMIT done(QPixmap());
}
return;
}
- Q_EMIT done(extractPixmap(icon, m_requestedSize).copy());
+ Q_EMIT done(extractPixmap(icon, m_requestedSize));
+}
+
+bool FaviconImageRequester::tryNextView()
+{
+ if (auto view = getNextViewForProcessing()) {
+ requestFaviconFromDatabase(view);
+ return true;
+ }
+
+ return false;
+}
+
+void FaviconImageRequester::requestFaviconFromDatabase(QPointer<QQuickWebEngineView> view)
+{
+ QtWebEngineCore::ProfileAdapter *profileAdapter = view->d_ptr->profileAdapter();
+ bool touchIconsEnabled = view->profile()->settings()->touchIconsEnabled();
+ if (isIconURL(m_imageSource)) {
+ profileAdapter->requestIconForIconURL(
+ m_imageSource, qMax(m_requestedSize.width(), m_requestedSize.height()),
+ touchIconsEnabled, [this](const QIcon &icon, const QUrl &) {
+ QMetaObject::invokeMethod(this, "iconRequestDone", Qt::QueuedConnection,
+ Q_ARG(const QIcon &, icon));
+ });
+ } else {
+ profileAdapter->requestIconForPageURL(
+ m_imageSource, qMax(m_requestedSize.width(), m_requestedSize.height()),
+ touchIconsEnabled, [this](const QIcon &icon, const QUrl &, const QUrl &) {
+ QMetaObject::invokeMethod(this, "iconRequestDone", Qt::QueuedConnection,
+ Q_ARG(const QIcon &, icon));
+ });
+ }
}
-int FaviconImageResponseRunnable::tryNextView()
+QPointer<QQuickWebEngineView> FaviconImageRequester::getNextViewForProcessing()
{
- for (; m_nextViewIndex < m_views->size(); ++m_nextViewIndex) {
- QQuickWebEngineView *view = m_views->at(m_nextViewIndex);
+ Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+
+ for (QPointer<QQuickWebEngineView> view : FaviconProviderHelper::instance()->views()) {
+ if (view.isNull())
+ continue;
if (view->profile()->isOffTheRecord())
continue;
+ if (m_processedViews.contains(view))
+ continue;
+ m_processedViews.append(view);
+ return view;
+ }
+ return nullptr;
+}
- requestIconOnUIThread(view);
+FaviconProviderHelper::FaviconProviderHelper()
+{
+ moveToThread(qApp->thread());
+}
- return m_nextViewIndex++;
- }
+FaviconProviderHelper *FaviconProviderHelper::instance()
+{
+ static FaviconProviderHelper instance;
+ return &instance;
+}
- return -1;
+void FaviconProviderHelper::attach(QPointer<QQuickWebEngineView> view)
+{
+ if (!m_views.contains(view))
+ m_views.append(view);
}
-void FaviconImageResponseRunnable::requestIconOnUIThread(QQuickWebEngineView *view)
+void FaviconProviderHelper::detach(QPointer<QQuickWebEngineView> view)
{
- QTimer *timer = new QTimer();
- timer->moveToThread(qApp->thread());
- timer->setSingleShot(true);
- QObject::connect(timer, &QTimer::timeout, [this, view, timer]() {
- QtWebEngineCore::ProfileAdapter *profileAdapter = view->d_ptr->profileAdapter();
- bool touchIconsEnabled = view->profile()->settings()->touchIconsEnabled();
- if (isIconURL(QUrl(m_id))) {
- profileAdapter->requestIconForIconURL(QUrl(m_id),
- qMax(m_requestedSize.width(), m_requestedSize.height()),
- touchIconsEnabled,
- [this](const QIcon &icon, const QUrl &) { iconRequestDone(icon); });
- } else {
- profileAdapter->requestIconForPageURL(QUrl(m_id),
- qMax(m_requestedSize.width(), m_requestedSize.height()),
- touchIconsEnabled,
- [this](const QIcon &icon, const QUrl &, const QUrl &) { iconRequestDone(icon); });
- }
- timer->deleteLater();
- });
- QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
+ m_views.removeAll(view);
}
-FaviconImageResponse::FaviconImageResponse()
+void FaviconProviderHelper::handleImageRequest(QPointer<FaviconImageResponse> faviconResponse)
{
- Q_EMIT finished();
+ Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+
+ if (faviconResponse.isNull())
+ return;
+
+ if (m_views.isEmpty()) {
+ QMetaObject::invokeMethod(faviconResponse, "handleDone", Qt::QueuedConnection,
+ Q_ARG(QPixmap, QPixmap()));
+ return;
+ }
+
+ auto view = findViewByImageSource(faviconResponse->imageSource());
+ if (view) {
+ QIcon icon = view->d_ptr->adapter->icon();
+ if (!icon.isNull()) {
+ QMetaObject::invokeMethod(
+ faviconResponse, "handleDone", Qt::QueuedConnection,
+ Q_ARG(QPixmap, extractPixmap(icon, faviconResponse->requestedSize())));
+ return;
+ }
+ }
+ startFaviconRequest(faviconResponse);
}
-FaviconImageResponse::FaviconImageResponse(const QString &id, const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views, QThreadPool *pool)
+QPointer<QQuickWebEngineView> FaviconProviderHelper::findViewByImageSource(const QUrl &imageSource) const
{
- if (QQuickWebEngineView *view = findViewById(id, views)) {
- QTimer *timer = new QTimer();
- timer->moveToThread(qApp->thread());
- timer->setSingleShot(true);
- QObject::connect(timer, &QTimer::timeout, [this, id, requestedSize, views, pool, view, timer]() {
- QIcon icon = view->d_ptr->adapter->icon();
- if (icon.isNull())
- startRunnable(id, requestedSize, views, pool);
- else
- handleDone(extractPixmap(icon, requestedSize).copy());
- timer->deleteLater();
- });
- QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
- } else {
- startRunnable(id, requestedSize, views, pool);
+ for (QPointer<QQuickWebEngineView> view : m_views) {
+ if (view.isNull())
+ continue;
+
+ if (isIconURL(imageSource)) {
+ if (view->icon() == QQuickWebEngineFaviconProvider::faviconProviderUrl(imageSource)) {
+ return view;
+ }
+ } else if (view->url() == imageSource) {
+ return view;
+ }
}
+
+ return nullptr;
}
-FaviconImageResponse::~FaviconImageResponse() { }
+void FaviconProviderHelper::startFaviconRequest(QPointer<FaviconImageResponse> faviconResponse)
+{
+ FaviconImageRequester *requester = new FaviconImageRequester(faviconResponse->imageSource(),
+ faviconResponse->requestedSize());
+
+ connect(requester, &FaviconImageRequester::done, [requester, faviconResponse](QPixmap pixmap) {
+ QMetaObject::invokeMethod(faviconResponse, "handleDone", Qt::QueuedConnection,
+ Q_ARG(QPixmap, pixmap));
+ requester->deleteLater();
+ });
+
+ requester->start();
+}
+
+FaviconImageResponse::FaviconImageResponse(const QUrl &imageSource, const QSize &requestedSize)
+ : m_imageSource(imageSource), m_requestedSize(requestedSize)
+{
+}
void FaviconImageResponse::handleDone(QPixmap pixmap)
{
- if (m_runnable)
- delete m_runnable;
m_image = pixmap.toImage();
Q_EMIT finished();
}
@@ -205,16 +249,6 @@ QQuickTextureFactory *FaviconImageResponse::textureFactory() const
return QQuickTextureFactory::textureFactoryForImage(m_image);
}
-void FaviconImageResponse::startRunnable(const QString &id, const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views, QThreadPool *pool)
-{
- m_runnable = new FaviconImageResponseRunnable(id, requestedSize, views);
- m_runnable->setAutoDelete(false);
- connect(m_runnable, &FaviconImageResponseRunnable::done, this,
- &FaviconImageResponse::handleDone);
- pool->start(m_runnable);
-}
-
QString QQuickWebEngineFaviconProvider::identifier()
{
return QStringLiteral("favicon");
@@ -238,17 +272,17 @@ QUrl QQuickWebEngineFaviconProvider::faviconProviderUrl(const QUrl &url)
return providerUrl;
}
-QQuickWebEngineFaviconProvider::QQuickWebEngineFaviconProvider() { }
-
-QQuickWebEngineFaviconProvider::~QQuickWebEngineFaviconProvider() { }
+QQuickWebEngineFaviconProvider::QQuickWebEngineFaviconProvider()
+{
+ connect(this, &QQuickWebEngineFaviconProvider::imageResponseRequested,
+ FaviconProviderHelper::instance(), &FaviconProviderHelper::handleImageRequest);
+}
QQuickImageResponse *
QQuickWebEngineFaviconProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
{
- if (m_views.empty())
- return new FaviconImageResponse;
-
- FaviconImageResponse *response = new FaviconImageResponse(id, requestedSize, &m_views, &m_pool);
+ FaviconImageResponse *response = new FaviconImageResponse(QUrl(id), requestedSize);
+ emit imageResponseRequested(response);
return response;
}
diff --git a/src/webenginequick/api/qquickwebenginefaviconprovider_p_p.h b/src/webenginequick/api/qquickwebenginefaviconprovider_p_p.h
index 89bfb6e73..f1d948413 100644
--- a/src/webenginequick/api/qquickwebenginefaviconprovider_p_p.h
+++ b/src/webenginequick/api/qquickwebenginefaviconprovider_p_p.h
@@ -17,8 +17,6 @@
#include <QtWebEngineQuick/private/qtwebenginequickglobal_p.h>
#include <QtCore/qlist.h>
-#include <QtCore/qrunnable.h>
-#include <QtCore/qthreadpool.h>
#include <QtGui/qimage.h>
#include <QtQuick/qquickimageprovider.h>
@@ -26,65 +24,84 @@ QT_BEGIN_NAMESPACE
class QQuickWebEngineView;
-class FaviconImageResponseRunnable : public QObject, public QRunnable
+class FaviconImageResponse : public QQuickImageResponse
{
Q_OBJECT
public:
- FaviconImageResponseRunnable(const QString &id, const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views);
- void run() override;
- void iconRequestDone(const QIcon &icon);
+ FaviconImageResponse(const QUrl &imageSource, const QSize &requestedSize);
-signals:
- void done(QPixmap pixmap);
+ QQuickTextureFactory *textureFactory() const override;
+ const QUrl &imageSource() const { return m_imageSource; }
+ const QSize &requestedSize() const { return m_requestedSize; }
-private:
- int tryNextView();
- void requestIconOnUIThread(QQuickWebEngineView *view);
+public slots:
+ void handleDone(QPixmap pixmap);
- QString m_id;
+private:
+ QImage m_image;
+ QUrl m_imageSource;
QSize m_requestedSize;
- QList<QQuickWebEngineView *> *m_views;
- int m_nextViewIndex = 0;
};
-class FaviconImageResponse : public QQuickImageResponse
+class FaviconImageRequester : public QObject
{
+ Q_OBJECT
+
public:
- FaviconImageResponse();
- FaviconImageResponse(const QString &id, const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views, QThreadPool *pool);
- ~FaviconImageResponse();
- void handleDone(QPixmap pixmap);
- QQuickTextureFactory *textureFactory() const override;
+ FaviconImageRequester(const QUrl &imageSource, const QSize &requestedSize);
+ void start();
+
+public slots:
+ void iconRequestDone(const QIcon &icon);
+
+signals:
+ void done(QPixmap pixmap);
private:
- void startRunnable(const QString &id, const QSize &requestedSize,
- QList<QQuickWebEngineView *> *views, QThreadPool *pool);
+ bool tryNextView();
+ void requestFaviconFromDatabase(QPointer<QQuickWebEngineView> view);
+ QPointer<QQuickWebEngineView> getNextViewForProcessing();
- FaviconImageResponseRunnable *m_runnable = nullptr;
- QImage m_image;
+ QUrl m_imageSource;
+ QSize m_requestedSize;
+ QList<QPointer<QQuickWebEngineView>> m_processedViews;
};
class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineFaviconProvider : public QQuickAsyncImageProvider
{
+ Q_OBJECT
+
public:
static QString identifier();
static QUrl faviconProviderUrl(const QUrl &);
QQuickWebEngineFaviconProvider();
- ~QQuickWebEngineFaviconProvider();
-
- void attach(QQuickWebEngineView *view) { m_views.append(view); }
- void detach(QQuickWebEngineView *view) { m_views.removeAll(view); }
-
QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override;
+signals:
+ void imageResponseRequested(QPointer<FaviconImageResponse> faviconResponse);
+};
+
+class Q_WEBENGINEQUICK_EXPORT FaviconProviderHelper : public QObject
+{
+ Q_OBJECT
+
+public:
+ static FaviconProviderHelper *instance();
+ void attach(QPointer<QQuickWebEngineView> view);
+ void detach(QPointer<QQuickWebEngineView> view);
+ const QList<QPointer<QQuickWebEngineView>> &views() const { return m_views; }
+
+public slots:
+ void handleImageRequest(QPointer<FaviconImageResponse> faviconResponse);
+
private:
- QThreadPool m_pool;
- QList<QQuickWebEngineView *> m_views;
+ FaviconProviderHelper();
+ void startFaviconRequest(QPointer<FaviconImageResponse> faviconResponse);
+ QPointer<QQuickWebEngineView> findViewByImageSource(const QUrl &imageSource) const;
+ QList<QPointer<QQuickWebEngineView>> m_views;
};
QT_END_NAMESPACE
diff --git a/src/webenginequick/api/qquickwebengineprofile.cpp b/src/webenginequick/api/qquickwebengineprofile.cpp
index 7c3d11fcf..edca5e99c 100644
--- a/src/webenginequick/api/qquickwebengineprofile.cpp
+++ b/src/webenginequick/api/qquickwebengineprofile.cpp
@@ -105,6 +105,8 @@ QT_BEGIN_NAMESPACE
The download item is parented by the profile. If it is not accepted, it
will be deleted immediately after the signal emission.
This signal cannot be used with a queued connection.
+
+ \note To use from C++ static_cast \a download to QWebEngineDownloadRequest
*/
/*!
@@ -113,6 +115,8 @@ QT_BEGIN_NAMESPACE
This signal is emitted whenever downloading stops, because it finished successfully, was
cancelled, or was interrupted (for example, because connectivity was lost).
The \a download argument holds the state of the finished download instance.
+
+ \note To use from C++ static_cast \a download to QWebEngineDownloadRequest
*/
/*!
diff --git a/src/webenginequick/api/qquickwebengineprofile.h b/src/webenginequick/api/qquickwebengineprofile.h
index 088a971e0..cbeb91147 100644
--- a/src/webenginequick/api/qquickwebengineprofile.h
+++ b/src/webenginequick/api/qquickwebengineprofile.h
@@ -144,7 +144,7 @@ private:
QQuickWebEngineSettings *settings() const;
void ensureQmlContext(const QObject *object);
- friend class FaviconImageResponseRunnable;
+ friend class FaviconImageRequester;
friend class QQuickWebEngineSingleton;
friend class QQuickWebEngineViewPrivate;
friend class QQuickWebEngineView;
diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp
index 78248346b..11ed33b5e 100644
--- a/src/webenginequick/api/qquickwebengineview.cpp
+++ b/src/webenginequick/api/qquickwebengineview.cpp
@@ -332,8 +332,7 @@ QQuickWebEngineViewPrivate::~QQuickWebEngineViewPrivate()
{
Q_ASSERT(m_profileInitialized);
m_profile->d_ptr->removeWebContentsAdapterClient(this);
- if (m_faviconProvider)
- m_faviconProvider->detach(q_ptr);
+ FaviconProviderHelper::instance()->detach(q_ptr);
bindViewAndDelegateItem(this, nullptr);
}
@@ -446,10 +445,10 @@ void QQuickWebEngineViewPrivate::contextMenuRequested(QWebEngineContextMenuReque
ui()->showMenu(menu);
}
-void QQuickWebEngineViewPrivate::navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame)
+void QQuickWebEngineViewPrivate::navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame, bool hasFrameData)
{
Q_Q(QQuickWebEngineView);
- auto request = new QWebEngineNavigationRequest(url, static_cast<QWebEngineNavigationRequest::NavigationType>(navigationType), isMainFrame);
+ auto request = new QWebEngineNavigationRequest(url, static_cast<QWebEngineNavigationRequest::NavigationType>(navigationType), isMainFrame, hasFrameData);
qmlEngine(q)->newQObject(request);
Q_EMIT q->navigationRequested(request);
@@ -951,16 +950,7 @@ void QQuickWebEngineViewPrivate::ensureContentsAdapter()
adapter->loadDefault();
}
- if (!m_faviconProvider) {
- QQmlEngine *engine = qmlEngine(q_ptr);
- // TODO: this is a workaround for QTBUG-65044
- if (!engine)
- return;
- m_faviconProvider = static_cast<QQuickWebEngineFaviconProvider *>(
- engine->imageProvider(QQuickWebEngineFaviconProvider::identifier()));
- m_faviconProvider->attach(q_ptr);
- Q_ASSERT(m_faviconProvider);
- }
+ FaviconProviderHelper::instance()->attach(q_ptr);
}
void QQuickWebEngineViewPrivate::initializationFinished()
@@ -2529,6 +2519,19 @@ QQmlComponent *QQuickWebEngineView::touchHandleDelegate() const
return d_ptr->m_touchHandleDelegate;
}
+QWebEngineFrame QQuickWebEngineView::mainFrame()
+{
+ Q_D(QQuickWebEngineView);
+ return QWebEngineFrame(d, d->adapter->mainFrameId());
+}
+
+QWebEngineFrame QQuickWebEngineView::findFrameByName(const QString &name)
+{
+ Q_D(QQuickWebEngineView);
+ auto maybeId = d->adapter->findFrameIdByName(name);
+ return QWebEngineFrame(d, maybeId.value_or(WebContentsAdapter::kInvalidFrameId));
+}
+
void QQuickWebEngineView::save(const QString &filePath,
QWebEngineDownloadRequest::SavePageFormat format) const
{
diff --git a/src/webenginequick/api/qquickwebengineview_p.h b/src/webenginequick/api/qquickwebengineview_p.h
index 0fdd9f787..37e39dfed 100644
--- a/src/webenginequick/api/qquickwebengineview_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p.h
@@ -19,6 +19,7 @@
#include <QtWebEngineCore/qwebenginequotarequest.h>
#include <QtWebEngineCore/qwebenginedesktopmediarequest.h>
#include <QtWebEngineCore/qwebenginedownloadrequest.h>
+#include <QtWebEngineCore/qwebengineframe.h>
#include <QtWebEngineQuick/private/qtwebenginequickglobal_p.h>
#include <QtGui/qcolor.h>
#include <QtQml/qqmlregistration.h>
@@ -91,6 +92,7 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineView : public QQuickItem {
Q_PROPERTY(qint64 renderProcessPid READ renderProcessPid NOTIFY renderProcessPidChanged FINAL REVISION(1,11))
Q_PROPERTY(QQmlComponent *touchHandleDelegate READ touchHandleDelegate WRITE
setTouchHandleDelegate NOTIFY touchHandleDelegateChanged REVISION(0) FINAL)
+ Q_PROPERTY(QWebEngineFrame mainFrame READ mainFrame FINAL REVISION(6, 8))
QML_NAMED_ELEMENT(WebEngineView)
QML_ADDED_IN_VERSION(1, 0)
QML_EXTRA_VERSION(2, 0)
@@ -472,6 +474,9 @@ QT_WARNING_POP
QQmlComponent *touchHandleDelegate() const;
void setTouchHandleDelegate(QQmlComponent *delegagte);
+ QWebEngineFrame mainFrame();
+ Q_REVISION(6, 8) Q_INVOKABLE QWebEngineFrame findFrameByName(const QString &name);
+
public Q_SLOTS:
void runJavaScript(const QString&, const QJSValue & = QJSValue());
Q_REVISION(1,3) void runJavaScript(const QString&, quint32 worldId, const QJSValue & = QJSValue());
@@ -572,8 +577,8 @@ private:
QScopedPointer<QQuickWebEngineViewPrivate> d_ptr;
friend class QQuickContextMenuBuilder;
- friend class FaviconImageResponse;
- friend class FaviconImageResponseRunnable;
+ friend class FaviconProviderHelper;
+ friend class FaviconImageRequester;
#if QT_CONFIG(accessibility)
friend class QQuickWebEngineViewAccessible;
#endif // QT_CONFIG(accessibility)
diff --git a/src/webenginequick/api/qquickwebengineview_p_p.h b/src/webenginequick/api/qquickwebengineview_p_p.h
index 66e53bdd0..cf4da7c40 100644
--- a/src/webenginequick/api/qquickwebengineview_p_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p_p.h
@@ -37,7 +37,6 @@ class WebContentsAdapter;
QT_BEGIN_NAMESPACE
class QQmlComponent;
-class QQuickWebEngineFaviconProvider;
class QQuickWebEngineScriptCollection;
class QQuickWebEngineSettings;
class QQuickWebEngineView;
@@ -88,7 +87,7 @@ public:
void requestFullScreenMode(const QUrl &origin, bool fullscreen) override;
bool isFullScreenMode() const override;
void contextMenuRequested(QWebEngineContextMenuRequest *request) override;
- void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame) override;
+ void navigationRequested(int navigationType, const QUrl &url, bool &accepted, bool isMainFrame, bool hasFrameData) override;
void javascriptDialog(QSharedPointer<QtWebEngineCore::JavaScriptDialogController>) override;
void runFileChooser(QSharedPointer<QtWebEngineCore::FilePickerController>) override;
void desktopMediaRequested(QtWebEngineCore::DesktopMediaController *) override;
@@ -178,7 +177,6 @@ private:
bool m_profileInitialized;
QWebEngineContextMenuRequest *m_contextMenuRequest;
QScopedPointer<QQuickWebEngineScriptCollection> m_scriptCollection;
- QPointer<QQuickWebEngineFaviconProvider> m_faviconProvider;
QQmlComponent *m_touchHandleDelegate;
};
diff --git a/src/webenginequick/doc/src/webengineframe.qdoc b/src/webenginequick/doc/src/webengineframe.qdoc
new file mode 100644
index 000000000..ef2a5c33d
--- /dev/null
+++ b/src/webenginequick/doc/src/webengineframe.qdoc
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmltype webEngineFrame
+ \instantiates QQuickWebEngineFrame
+ \brief webEngineFrame provides information about and control over a page frame.
+ \since 6.8
+ \ingroup qmlvaluetypes
+ \inqmlmodule QtWebEngine
+
+ A web engine frame represents a single frame within a web page, such as those created by
+ \c <frame> or \c <iframe> HTML elements.
+ An active \l WebEngineView has one or more frames arranged in a tree structure. The top-level
+ frame, the root of this tree, can be accessed through the view's \l {WebEngineView::mainFrame}
+ {mainFrame} property.
+
+ A frame's lifetime is, at most, as long as the \l WebEngineView object that produced it.
+ However, frames may be created and deleted spontaneously and dynamically, for example through
+ navigation and script execution.
+*/
+
+/*!
+ \qmlproperty bool webEngineFrame::isValid
+
+ Returns \c{true} if this object represents an existing frame; \c{false} otherwise.
+
+ Once a frame is invalid, it never becomes valid again.
+*/
+
+/*!
+ \qmlproperty string webEngineFrame::name
+
+ Returns the frame name; that is, what would be returned by \c window.name in JavaScript.
+
+ If the frame could not be found, returns an empty string.
+
+ \sa htmlName
+*/
+
+/*!
+ \qmlproperty string webEngineFrame::htmlName
+
+ Returns the value of the frame's \c name HTML attribute, or an empty string if it has none.
+
+ If the frame could not be found, returns an empty string.
+
+ \sa name
+*/
+
+/*!
+ \qmlproperty url webEngineFrame::url
+
+ Returns the URL of the content currently loaded in this frame.
+
+ If the frame could not be found, returns an empty URL.
+*/
+
+/*!
+ \qmlproperty size webEngineFrame::size
+
+ Returns the size of the frame within the viewport.
+
+ If the frame could not be found, returns a default size with dimensions (-1, -1).
+*/
diff --git a/src/webenginequick/doc/src/webengineview_lgpl.qdoc b/src/webenginequick/doc/src/webengineview_lgpl.qdoc
index eeae34dcc..dbaea62e4 100644
--- a/src/webenginequick/doc/src/webengineview_lgpl.qdoc
+++ b/src/webenginequick/doc/src/webengineview_lgpl.qdoc
@@ -1551,6 +1551,23 @@
*/
/*!
+ \qmlproperty webEngineFrame WebEngineView::mainFrame
+ \since QtWebEngine 6.8
+
+ The main, top-level frame of the page. All other frames on this page are accessible
+ as children of the main frame.
+ */
+
+/*!
+ \qmlmethod webEngineFrame WebEngineView::findFrameByName(string name)
+ \since QtWebEngine 6.8
+
+ Returns the frame with the given \a name. If there are multiple frames with the same
+ name, which one is returned is arbitrary. If no frame was found, returns an
+ \l{webEngineFrame::isValid}{invalid} frame.
+*/
+
+/*!
\qmlmethod void WebEngineView::save(const QString &filePath, QWebEngineDownloadRequest::SavePageFormat format)
\since QtWebEngine 6.6
diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt
index 5908756b4..eb8e9266f 100644
--- a/tests/auto/core/CMakeLists.txt
+++ b/tests/auto/core/CMakeLists.txt
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(qwebenginecookiestore)
+add_subdirectory(qwebengineframe)
add_subdirectory(qwebengineloadinginfo)
add_subdirectory(qwebenginesettings)
if(QT_FEATURE_ssl)
diff --git a/tests/auto/core/qwebengineframe/CMakeLists.txt b/tests/auto/core/qwebengineframe/CMakeLists.txt
new file mode 100644
index 000000000..d02b4307d
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(../../util/util.cmake)
+
+qt_internal_add_test(tst_qwebengineframe
+ SOURCES
+ tst_qwebengineframe.cpp
+ LIBRARIES
+ Qt::WebEngineCore
+ Qt::WebEngineWidgets
+ Test::Util
+)
+
+qt_internal_add_resource(tst_qwebengineframe "tst_qwebengineframe"
+ PREFIX
+ "/"
+ FILES
+ "resources/frameset.html"
+ "resources/iframes.html"
+ "resources/nesting-iframe.html"
+)
diff --git a/tests/auto/core/qwebengineframe/resources/frameset.html b/tests/auto/core/qwebengineframe/resources/frameset.html
new file mode 100644
index 000000000..53f5e6638
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/resources/frameset.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head><title>Test-title</title></head>
+ <script>
+ window.name = 'test-main-frame'
+ onload = (e) => {
+ const frames = window.frames;
+ for (let i = 0; i < frames.length; i++) {
+ frames[i].name = 'test-subframe' + i;
+ }
+ };
+ </script>
+ <frameset cols="50%, 50%">
+ <frameset cols="50%, 50%">
+ <frame style="border: red dashed 1em;"/>
+ <frame style="border: green dashed 1em;"/>
+ </frameset>
+ <frame style="border: blue solid 1em;"/>
+ </frameset>
+</html>
diff --git a/tests/auto/core/qwebengineframe/resources/iframes.html b/tests/auto/core/qwebengineframe/resources/iframes.html
new file mode 100644
index 000000000..648acb166
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/resources/iframes.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+ <head><title>Test-title</title></head>
+ <script>
+ window.name = 'test-main-frame'
+ onload = (e) => {
+ const frames = window.frames;
+ for (let i = 0; i < frames.length; i++) {
+ frames[i].name = 'test-subframe' + i;
+ }
+ };
+ </script>
+ <body>
+ <iframe name="iframe0-300x200" width="300" height="200"></iframe>
+ <iframe name="iframe1-350x250" width="350" height="250"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/core/qwebengineframe/resources/nesting-iframe.html b/tests/auto/core/qwebengineframe/resources/nesting-iframe.html
new file mode 100644
index 000000000..cd784e3dd
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/resources/nesting-iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+ <head><title>Test-title</title></head>
+ <body>
+ <iframe name="iframe2-parent" src="iframes.html"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
new file mode 100644
index 000000000..b70a655d3
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
@@ -0,0 +1,180 @@
+/*
+ Copyright (C) 2024 The Qt Company Ltd.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <util.h>
+
+#include <QtTest/QtTest>
+
+#include <QtWebEngineCore/qwebengineframe.h>
+
+class tst_QWebEngineFrame : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void mainFrame();
+ void findFrameByName();
+ void isValid();
+ void name();
+ void htmlName();
+ void children();
+ void childrenOfInvalidFrame();
+ void url();
+ void size();
+
+private:
+};
+
+void tst_QWebEngineFrame::mainFrame()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto frame = page.mainFrame();
+ QVERIFY(frame.isValid());
+}
+
+void tst_QWebEngineFrame::findFrameByName()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/iframes.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto maybeFrame = page.findFrameByName("test-subframe0");
+ QVERIFY(maybeFrame.has_value());
+ QCOMPARE(maybeFrame->name(), "test-subframe0");
+ QVERIFY(!page.findFrameByName("foobar").has_value());
+}
+
+void tst_QWebEngineFrame::isValid()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/iframes.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto firstPageSubframe = page.findFrameByName("test-subframe0");
+ QVERIFY(firstPageSubframe && firstPageSubframe->isValid());
+
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!firstPageSubframe->isValid());
+}
+
+void tst_QWebEngineFrame::name()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ QCOMPARE(page.mainFrame().name(), "test-main-frame");
+ auto children = page.mainFrame().children();
+ QCOMPARE(children.at(0).name(), "test-subframe0");
+ QCOMPARE(children.at(1).name(), "test-subframe1");
+ QCOMPARE(children.at(2).name(), "test-subframe2");
+
+ page.load(QUrl("qrc:/resources/iframes.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!children.at(0).isValid());
+ QCOMPARE(children.at(0).name(), QString());
+}
+
+void tst_QWebEngineFrame::htmlName()
+{
+ 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();
+ QCOMPARE(children.at(0).name(), "test-subframe0");
+ QCOMPARE(children.at(0).htmlName(), "iframe0-300x200");
+ QCOMPARE(children.at(1).name(), "test-subframe1");
+ QCOMPARE(children.at(1).htmlName(), "iframe1-350x250");
+
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!children.at(0).isValid());
+ QCOMPARE(children.at(0).htmlName(), QString());
+}
+
+void tst_QWebEngineFrame::children()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto frame = page.mainFrame();
+ auto children = frame.children();
+ QCOMPARE(children.size(), 3);
+ for (auto child : children) {
+ QVERIFY(child.isValid());
+ }
+}
+
+void tst_QWebEngineFrame::childrenOfInvalidFrame()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/nesting-iframe.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto nestedFrame = page.mainFrame().children().at(0);
+ QCOMPARE(nestedFrame.children().size(), 2);
+
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!nestedFrame.isValid());
+ QVERIFY(nestedFrame.children().empty());
+}
+
+void tst_QWebEngineFrame::url()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/nesting-iframe.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto children = page.mainFrame().children();
+ QCOMPARE(children.at(0).url(), QUrl("qrc:/resources/iframes.html"));
+
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!children.at(0).isValid());
+ QCOMPARE(children.at(0).url(), QUrl());
+}
+
+void tst_QWebEngineFrame::size()
+{
+ QWebEnginePage page;
+ QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+ page.load(QUrl("qrc:/resources/iframes.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+ auto frame1 = *page.findFrameByName("test-subframe0");
+ auto size1 = frame1.size();
+ auto size2 = page.findFrameByName("test-subframe1")->size();
+ QCOMPARE(size1, QSizeF(300, 200));
+ QCOMPARE(size2, QSizeF(350, 250));
+
+ page.load(QUrl("qrc:/resources/frameset.html"));
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QVERIFY(!frame1.isValid());
+ QCOMPARE(frame1.size(), QSizeF());
+}
+
+QTEST_MAIN(tst_QWebEngineFrame)
+
+#include "tst_qwebengineframe.moc"
diff --git a/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt
index fa81ba8df..dd2f28857 100644
--- a/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt
+++ b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt
@@ -8,6 +8,7 @@ qt_internal_add_test(tst_qwebengineglobalsettings
SOURCES
tst_qwebengineglobalsettings.cpp
LIBRARIES
+ Qt::Network
Qt::WebEngineCore
Test::HttpServer
Qt::WebEngineWidgets
diff --git a/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
index 0af85a711..5b6b64778 100644
--- a/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
+++ b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
@@ -3,6 +3,8 @@
#include <QtTest>
#include <widgetutil.h>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
#include <QWebEngineProfile>
#include <QWebEnginePage>
#include <QWebEngineGlobalSettings>
@@ -68,6 +70,15 @@ void tst_QWebEngineGlobalSettings::dnsOverHttps_data()
void tst_QWebEngineGlobalSettings::dnsOverHttps()
{
+ const QUrl url = QStringLiteral("https://google.com/");
+ // Verify network access with NAM because the result of loadFinished signal
+ // is used to verify that the DNS resolution was successful.
+ QNetworkAccessManager nam;
+ QSignalSpy namSpy(&nam, &QNetworkAccessManager::finished);
+ QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(url)));
+ if (!namSpy.wait(20000) || reply->error() != QNetworkReply::NoError)
+ QSKIP("Couldn't load page from network, skipping test.");
+
QFETCH(QWebEngineGlobalSettings::SecureDnsMode, dnsMode);
QFETCH(QString, uriTemplate);
QFETCH(bool, isMockDnsServerCalledExpected);
@@ -105,10 +116,8 @@ void tst_QWebEngineGlobalSettings::dnsOverHttps()
connect(&page, &QWebEnginePage::loadFinished, this,
[&isLoadSuccessful](bool ok) { isLoadSuccessful = ok; });
- page.load(QUrl("https://google.com/"));
- if (!loadSpy.wait(20000)) {
- QSKIP("Couldn't load page from network, skipping test.");
- }
+ page.load(url);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000);
QTRY_COMPARE(isMockDnsServerCalled, isMockDnsServerCalledExpected);
QCOMPARE(isLoadSuccessful, isDnsResolutionSuccessExpected);
diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp
index cfa75f0bf..4d9d02bca 100644
--- a/tests/auto/quick/publicapi/tst_publicapi.cpp
+++ b/tests/auto/quick/publicapi/tst_publicapi.cpp
@@ -25,6 +25,7 @@
#include <QtWebEngineCore/QWebEngineScript>
#include <QtWebEngineCore/QWebEngineLoadingInfo>
#include <QtWebEngineCore/QWebEngineWebAuthUxRequest>
+#include <QtWebEngineCore/QWebEngineFrame>
#include <private/qquickwebengineview_p.h>
#include <private/qquickwebengineaction_p.h>
#include <private/qquickwebengineclientcertificateselection_p.h>
@@ -76,6 +77,7 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject *
<< &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject
<< &QWebEngineWebAuthUxRequest::staticMetaObject
<< &QWebEngineWebAuthPinRequest::staticMetaObject
+ << &QWebEngineFrame::staticMetaObject
;
static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>()
@@ -328,6 +330,7 @@ static const QStringList expectedAPI = QStringList()
<< "QWebEngineNavigationRequest.action --> QWebEngineNavigationRequest::NavigationRequestAction"
<< "QWebEngineNavigationRequest.actionChanged() --> void"
<< "QWebEngineNavigationRequest.isMainFrame --> bool"
+ << "QWebEngineNavigationRequest.hasFormData --> bool"
<< "QWebEngineNavigationRequest.navigationType --> QWebEngineNavigationRequest::NavigationType"
<< "QWebEngineNavigationRequest.url --> QUrl"
<< "QWebEngineNavigationRequest.AcceptRequest --> NavigationRequestAction"
@@ -714,6 +717,7 @@ static const QStringList expectedAPI = QStringList()
<< "QQuickWebEngineView.featurePermissionRequested(QUrl,QQuickWebEngineView::Feature) --> void"
<< "QQuickWebEngineView.fileDialogRequested(QQuickWebEngineFileDialogRequest*) --> void"
<< "QQuickWebEngineView.fileSystemAccessRequested(QWebEngineFileSystemAccessRequest) --> void"
+ << "QQuickWebEngineView.findFrameByName(QString) --> QWebEngineFrame"
<< "QQuickWebEngineView.findText(QString) --> void"
<< "QQuickWebEngineView.findText(QString,FindFlags) --> void"
<< "QQuickWebEngineView.findText(QString,FindFlags,QJSValue) --> void"
@@ -743,6 +747,7 @@ static const QStringList expectedAPI = QStringList()
<< "QQuickWebEngineView.loadProgressChanged() --> void"
<< "QQuickWebEngineView.loading --> bool"
<< "QQuickWebEngineView.loadingChanged(QWebEngineLoadingInfo) --> void"
+ << "QQuickWebEngineView.mainFrame --> QWebEngineFrame"
<< "QQuickWebEngineView.navigationRequested(QWebEngineNavigationRequest*) --> void"
<< "QQuickWebEngineView.newWindowRequested(QQuickWebEngineNewWindowRequest*) --> void"
<< "QQuickWebEngineView.AcceptRequest --> NavigationRequestAction"
@@ -879,6 +884,11 @@ static const QStringList expectedAPI = QStringList()
<< "QQuickWebEngineSettings.DisallowImageAnimation --> ImageAnimationPolicy"
<< "QQuickWebEngineSettings.imageAnimationPolicy --> QQuickWebEngineSettings::ImageAnimationPolicy"
<< "QQuickWebEngineSettings.imageAnimationPolicyChanged() --> void"
+ << "QWebEngineFrame.htmlName --> QString"
+ << "QWebEngineFrame.isValid --> bool"
+ << "QWebEngineFrame.name --> QString"
+ << "QWebEngineFrame.size --> QSizeF"
+ << "QWebEngineFrame.url --> QUrl"
;
static bool isCheckedEnum(QMetaType t)
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 39a28759c..f1d64776b 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -117,6 +117,7 @@ private Q_SLOTS:
void comboBoxPopupPositionAfterChildMove_data();
void comboBoxPopupPositionAfterChildMove();
void acceptNavigationRequest();
+ void acceptNavigationRequestWithFormData();
void acceptNavigationRequestNavigationType();
void acceptNavigationRequestRelativeToNothing();
#ifndef Q_OS_MACOS
@@ -636,6 +637,7 @@ public:
QWebEngineNavigationRequest::NavigationType type;
QUrl url;
bool isMainFrame;
+ bool hasFormData;
};
QList<Navigation> navigations;
@@ -653,6 +655,7 @@ private Q_SLOTS:
n.url = request.url();
n.type = request.navigationType();
n.isMainFrame = request.isMainFrame();
+ n.hasFormData = request.hasFormData();
navigations.append(n);
request.accept();
}
@@ -670,9 +673,33 @@ private Q_SLOTS:
}
};
-void tst_QWebEnginePage::acceptNavigationRequestNavigationType()
+void tst_QWebEnginePage::acceptNavigationRequestWithFormData()
{
+ QWebEngineProfile profile;
+ profile.installUrlSchemeHandler("echo", new EchoingUrlSchemeHandler(&profile));
+ TestPage page(nullptr, &profile);
+ QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+
+ page.setHtml(QString("<html><body><form name='tstform' action='foo' method='post'>"
+ "<input type='text'><input type='submit'></form></body></html>"),
+ QUrl("echo:/"));
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000);
+ QCOMPARE(page.navigations[0].type, QWebEngineNavigationRequest::TypedNavigation);
+ QVERIFY(!page.navigations[0].hasFormData);
+ evaluateJavaScriptSync(&page, "tstform.submit();");
+ QTRY_COMPARE(loadSpy.size(), 2);
+ QCOMPARE(page.navigations[1].type, QWebEngineNavigationRequest::FormSubmittedNavigation);
+ QVERIFY(page.navigations[1].hasFormData);
+
+ page.triggerAction(QWebEnginePage::Reload);
+ QTRY_COMPARE(loadSpy.size(), 3);
+ QCOMPARE(page.navigations[2].type, QWebEngineNavigationRequest::ReloadNavigation);
+ QVERIFY(page.navigations[2].hasFormData);
+}
+
+void tst_QWebEnginePage::acceptNavigationRequestNavigationType()
+{
TestPage page;
QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index 095d4c8f2..5bd33332b 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -188,6 +188,7 @@ void tst_QWebEngineProfile::clearDataFromCache()
QVERIFY(server.start());
AutoDir cacheDir("./tst_QWebEngineProfile_clearDataFromCache");
+ QVERIFY(!cacheDir.exists("Cache"));
QWebEngineProfile profile(QStringLiteral("clearDataFromCache"));
QSignalSpy cacheSpy(&profile, &QWebEngineProfile::clearHttpCacheCompleted);
@@ -201,9 +202,10 @@ void tst_QWebEngineProfile::clearDataFromCache()
QVERIFY(cacheDir.exists("Cache"));
qint64 sizeBeforeClear = totalSize(cacheDir);
+ QCOMPARE_GT(sizeBeforeClear, 0);
profile.clearHttpCache();
QTRY_COMPARE(cacheSpy.size(), 1);
- QVERIFY(sizeBeforeClear > totalSize(cacheDir));
+ QCOMPARE_GT(sizeBeforeClear, totalSize(cacheDir));
(void)server.stop();
}