diff options
| author | Moss Heim <moss.heim@qt.io> | 2024-03-22 11:24:02 +0100 |
|---|---|---|
| committer | Moss Heim <moss.heim@qt.io> | 2024-05-03 10:38:40 +0200 |
| commit | 002641063fadb1cfd3cd9ae12d1d83e4fb0f1e86 (patch) | |
| tree | 2b882a7fe5a595e4ebcba230ee40dbf39e4b68fc /src | |
| parent | 45dd5da005ef6df6912179554808b7636a6a801b (diff) | |
Add QWebEngineFrame and basic API
This object represents a frame on a web page, e.g. a <frame> or <iframe>
element.
Frames can be found through QWebEnginePage::mainFrame()
and findFrameByName(). Also provides some basic getters
on QWebEngineFrame and tests.
Change-Id: If3905c6ecd14cf6c3508c65edc98f49415739489
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/api/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/core/api/qwebengineframe.cpp | 118 | ||||
| -rw-r--r-- | src/core/api/qwebengineframe.h | 49 | ||||
| -rw-r--r-- | src/core/api/qwebenginepage.cpp | 27 | ||||
| -rw-r--r-- | src/core/api/qwebenginepage.h | 5 | ||||
| -rw-r--r-- | src/core/web_contents_adapter.cpp | 74 | ||||
| -rw-r--r-- | src/core/web_contents_adapter.h | 14 |
7 files changed, 288 insertions, 0 deletions
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..1378a5cbc --- /dev/null +++ b/src/core/api/qwebengineframe.h @@ -0,0 +1,49 @@ +// 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 <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 +{ +public: + 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; + + 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/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp index 686cee391..c2a737d72 100644 --- a/src/core/api/qwebenginepage.cpp +++ b/src/core/api/qwebenginepage.cpp @@ -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/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; |
