diff options
Diffstat (limited to 'src/core/api/qwebenginepage.cpp')
-rw-r--r-- | src/core/api/qwebenginepage.cpp | 750 |
1 files changed, 452 insertions, 298 deletions
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp index c17276a1f..c2a737d72 100644 --- a/src/core/api/qwebenginepage.cpp +++ b/src/core/api/qwebenginepage.cpp @@ -1,63 +1,32 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 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 "qwebenginepage.h" +#include "authenticator_request_dialog_controller.h" #include "qwebenginepage_p.h" #include "qwebenginecertificateerror.h" +#include "qwebenginedesktopmediarequest.h" +#include "qwebenginefilesystemaccessrequest.h" #include "qwebenginefindtextresult.h" #include "qwebenginefullscreenrequest.h" #include "qwebenginehistory.h" #include "qwebenginehistory_p.h" +#include "qwebenginehttprequest.h" #include "qwebengineloadinginfo.h" #include "qwebenginenavigationrequest.h" #include "qwebenginenewwindowrequest.h" #include "qwebenginenewwindowrequest_p.h" #include "qwebengineprofile.h" #include "qwebengineprofile_p.h" -#include "qwebenginequotarequest.h" #include "qwebengineregisterprotocolhandlerrequest.h" #include "qwebenginescript.h" #include "qwebenginescriptcollection_p.h" #include "qwebenginesettings.h" +#include "qwebenginewebauthuxrequest.h" #include "authentication_dialog_controller.h" +#include "autofill_popup_controller.h" #include "color_chooser_controller.h" #include "find_text_helper.h" #include "file_picker_controller.h" @@ -66,6 +35,8 @@ #include "render_view_context_menu_qt.h" #include "render_widget_host_view_qt_delegate.h" #include "render_widget_host_view_qt_delegate_client.h" +#include "render_widget_host_view_qt_delegate_item.h" +#include "touch_selection_menu_controller.h" #include "web_contents_adapter.h" #include <QAction> @@ -74,60 +45,19 @@ #include <QClipboard> #include <QKeyEvent> #include <QIcon> + #include <QLoggingCategory> #include <QMimeData> +#include <QtCore/QPointer> +#include <QRect> #include <QTimer> #include <QUrl> +#include <QVariant> QT_BEGIN_NAMESPACE using namespace QtWebEngineCore; -// add temporary dummy code to cover the case when page is loading and there is no view -class DummyDelegate : public QObject, public QtWebEngineCore::RenderWidgetHostViewQtDelegate -{ -public: - DummyDelegate(RenderWidgetHostViewQtDelegateClient *client) : m_delegateClient(client) {}; - ~DummyDelegate() = default; - void initAsPopup(const QRect &) override { Q_UNREACHABLE(); } - QRectF viewGeometry() const override { return QRectF(m_pos, m_size); } - void setKeyboardFocus() override { } - bool hasKeyboardFocus() override { return false; } - void lockMouse() override { Q_UNREACHABLE(); } - void unlockMouse() override { Q_UNREACHABLE(); } - void show() override { m_delegateClient->notifyShown(); } - void hide() override { m_delegateClient->notifyHidden(); } - bool isVisible() const override { Q_UNREACHABLE(); } - QWindow *window() const override { return nullptr; } - void updateCursor(const QCursor &cursor) override - { - Q_UNUSED(cursor); - /*setCursor(cursor);*/ - } - void resize(int width, int height) override - { - m_size = QSize(width, height); - m_delegateClient->visualPropertiesChanged(); - } - void move(const QPoint &) override { Q_UNREACHABLE(); } - void inputMethodStateChanged(bool, bool) override { } - void setInputMethodHints(Qt::InputMethodHints) override { } - void setClearColor(const QColor &) override { } - void adapterClientChanged(WebContentsAdapterClient *) override { } - bool copySurface(const QRect &, const QSize &, QImage &) - { - Q_UNREACHABLE(); - return false; - } - QRect windowGeometry() const override { return QRect(m_pos, m_size); } - bool forwardEvent(QEvent *ev) { return m_delegateClient->forwardEvent(ev); } - -private: - RenderWidgetHostViewQtDelegateClient *m_delegateClient; - QPoint m_pos; - QSize m_size; -}; - static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition) { switch (disposition) { @@ -178,8 +108,11 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) { memset(actions, 0, sizeof(actions)); +#if QT_DEPRECATED_SINCE(6, 5) qRegisterMetaType<QWebEngineQuotaRequest>(); +#endif qRegisterMetaType<QWebEngineRegisterProtocolHandlerRequest>(); + qRegisterMetaType<QWebEngineFileSystemAccessRequest>(); qRegisterMetaType<QWebEngineFindTextResult>(); // See setVisible(). @@ -200,6 +133,14 @@ QWebEnginePagePrivate::~QWebEnginePagePrivate() RenderWidgetHostViewQtDelegate *QWebEnginePagePrivate::CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client) { + if (view) + return view->CreateRenderWidgetHostViewQtDelegate(client); + delegateItem = new QtWebEngineCore::RenderWidgetHostViewQtDelegateItem(client, false); + return delegateItem; +} + +RenderWidgetHostViewQtDelegate *QWebEnginePagePrivate::CreateRenderWidgetHostViewQtDelegateForPopup(RenderWidgetHostViewQtDelegateClient *client) +{ // Set the QWebEngineView as the parent for a popup delegate, so that the new popup window // responds properly to clicks in case the QWebEngineView is inside a modal QDialog. Setting the // parent essentially notifies the OS that the popup window is part of the modal session, and @@ -207,7 +148,9 @@ RenderWidgetHostViewQtDelegate *QWebEnginePagePrivate::CreateRenderWidgetHostVie // The new delegate will not be deleted by the parent view though, because we unset the parent // when the parent is destroyed. The delegate will be destroyed by Chromium when the popup is // dismissed. - return view ? view->CreateRenderWidgetHostViewQtDelegate(client) : new DummyDelegate(client); + return view + ? view->CreateRenderWidgetHostViewQtDelegateForPopup(client) + : new QtWebEngineCore::RenderWidgetHostViewQtDelegateItem(client, true); } void QWebEnginePagePrivate::initializationFinished() @@ -277,6 +220,12 @@ void QWebEnginePagePrivate::selectionChanged() }); } +void QWebEnginePagePrivate::zoomUpdateIsNeeded() +{ + Q_Q(QWebEnginePage); + q->setZoomFactor(defaultZoomFactor); +} + void QWebEnginePagePrivate::recentlyAudibleChanged(bool recentlyAudible) { Q_Q(QWebEnginePage); @@ -321,6 +270,8 @@ void QWebEnginePagePrivate::loadFinished(QWebEngineLoadingInfo info) void QWebEnginePagePrivate::didPrintPageToPdf(const QString &filePath, bool success) { + Q_Q(QWebEnginePage); + Q_EMIT q->pdfPrintingFinished(filePath, success); if (view) view->didPrintPageToPdf(filePath, success); } @@ -351,20 +302,8 @@ QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebC if (!newWebContents->webContents()) return newPage->d_func()->adapter; // Reuse existing adapter - // Mark the new page as being in the process of being adopted, so that a second mouse move event - // sent by newWebContents->initialize() gets filtered in RenderWidgetHostViewQt::forwardEvent. - // The first mouse move event is being sent by q->createWindow(). This is necessary because - // Chromium does not get a mouse move acknowledgment message between the two events, and - // InputRouterImpl::ProcessMouseAck is not executed, thus all subsequent mouse move events - // get coalesced together, and don't get processed at all. - // The mouse move events are actually sent as a result of show() being called on - // RenderWidgetHostViewQtDelegateWidget, both when creating the window and when initialize is - // called. - newPage->d_func()->m_isBeingAdopted = true; - - // Overwrite the new page's WebContents with ours. - newPage->d_func()->adapter = newWebContents; - newWebContents->setClient(newPage->d_func()); + if (!newPage->d_func()->adoptWebContents(newWebContents.get())) + return nullptr; if (!initialGeometry.isEmpty()) emit newPage->geometryChangeRequested(initialGeometry); @@ -397,6 +336,109 @@ void QWebEnginePagePrivate::createNewWindow(WindowOpenDisposition disposition, b Q_EMIT q->newWindowRequested(request); } +QString QWebEnginePagePrivate::actionText(int action) +{ + switch (action) { + case QWebEnginePage::Back: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Back); + case QWebEnginePage::Forward: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Forward); + case QWebEnginePage::Stop: + return QWebEnginePage::tr("Stop"); + case QWebEnginePage::Reload: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Reload); + case QWebEnginePage::ReloadAndBypassCache: + return QWebEnginePage::tr("Reload and Bypass Cache"); + case QWebEnginePage::Cut: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Cut); + case QWebEnginePage::Copy: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Copy); + case QWebEnginePage::Paste: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Paste); + case QWebEnginePage::Undo: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Undo); + case QWebEnginePage::Redo: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Redo); + case QWebEnginePage::SelectAll: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::SelectAll); + case QWebEnginePage::PasteAndMatchStyle: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::PasteAndMatchStyle); + case QWebEnginePage::OpenLinkInThisWindow: + return QWebEnginePage::tr("Open link in this window"); + case QWebEnginePage::OpenLinkInNewWindow: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::OpenLinkInNewWindow); + case QWebEnginePage::OpenLinkInNewTab: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::OpenLinkInNewTab); + case QWebEnginePage::OpenLinkInNewBackgroundTab: + return QWebEnginePage::tr("Open link in new background tab"); + case QWebEnginePage::CopyLinkToClipboard: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyLinkToClipboard); + case QWebEnginePage::DownloadLinkToDisk: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadLinkToDisk); + case QWebEnginePage::CopyImageToClipboard: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyImageToClipboard); + case QWebEnginePage::CopyImageUrlToClipboard: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyImageUrlToClipboard); + case QWebEnginePage::DownloadImageToDisk: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadImageToDisk); + case QWebEnginePage::CopyMediaUrlToClipboard: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyMediaUrlToClipboard); + case QWebEnginePage::ToggleMediaControls: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ToggleMediaControls); + case QWebEnginePage::ToggleMediaLoop: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ToggleMediaLoop); + case QWebEnginePage::ToggleMediaPlayPause: + return QWebEnginePage::tr("Toggle Play/Pause"); + case QWebEnginePage::ToggleMediaMute: + return QWebEnginePage::tr("Toggle Mute"); + case QWebEnginePage::DownloadMediaToDisk: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadMediaToDisk); + case QWebEnginePage::InspectElement: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::InspectElement); + case QWebEnginePage::ExitFullScreen: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ExitFullScreen); + case QWebEnginePage::RequestClose: + return QWebEnginePage::tr("Close Page"); + case QWebEnginePage::Unselect: + return QWebEnginePage::tr("Unselect"); + case QWebEnginePage::SavePage: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::SavePage); + case QWebEnginePage::ViewSource: + return RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ViewSource); + case QWebEnginePage::ToggleBold: + return QWebEnginePage::tr("&Bold"); + case QWebEnginePage::ToggleItalic: + return QWebEnginePage::tr("&Italic"); + case QWebEnginePage::ToggleUnderline: + return QWebEnginePage::tr("&Underline"); + case QWebEnginePage::ToggleStrikethrough: + return QWebEnginePage::tr("&Strikethrough"); + case QWebEnginePage::AlignLeft: + return QWebEnginePage::tr("Align &Left"); + case QWebEnginePage::AlignCenter: + return QWebEnginePage::tr("Align &Center"); + case QWebEnginePage::AlignRight: + return QWebEnginePage::tr("Align &Right"); + case QWebEnginePage::AlignJustified: + return QWebEnginePage::tr("Align &Justified"); + case QWebEnginePage::Indent: + return QWebEnginePage::tr("&Indent"); + case QWebEnginePage::Outdent: + return QWebEnginePage::tr("&Outdent"); + case QWebEnginePage::InsertOrderedList: + return QWebEnginePage::tr("Insert &Ordered List"); + case QWebEnginePage::InsertUnorderedList: + return QWebEnginePage::tr("Insert &Unordered List"); + case QWebEnginePage::ChangeTextDirectionLTR: + return QWebEnginePage::tr("Change text direction left to right"); + case QWebEnginePage::ChangeTextDirectionRTL: + return QWebEnginePage::tr("Change text direction right to left"); + default: + break; + } + return {}; +} + class WebContentsAdapterOwner : public QObject { public: @@ -409,22 +451,18 @@ private: AdapterPtr adapter; }; -void QWebEnginePagePrivate::adoptWebContents(WebContentsAdapter *webContents) +bool QWebEnginePagePrivate::adoptWebContents(WebContentsAdapter *webContents) { - if (!webContents) { - qWarning("Trying to open an empty request, it was either already used or was invalidated." - "\nYou must complete the request synchronously within the newPageRequested signal handler." - " If a view hasn't been adopted before returning, the request will be invalidated."); - return; - } - + Q_ASSERT(webContents); if (webContents->profileAdapter() && profileAdapter() != webContents->profileAdapter()) { qWarning("Can not adopt content from a different WebEngineProfile."); - return; + return false; } m_isBeingAdopted = true; + webContents->setRequestInterceptor(adapter->requestInterceptor()); + // This throws away the WebContentsAdapter that has been used until now. // All its states, particularly the loading URL, are replaced by the adopted WebContentsAdapter. WebContentsAdapterOwner *adapterOwner = new WebContentsAdapterOwner(adapter->sharedFromThis()); @@ -432,6 +470,7 @@ void QWebEnginePagePrivate::adoptWebContents(WebContentsAdapter *webContents) adapter = webContents->sharedFromThis(); adapter->setClient(this); + return true; } bool QWebEnginePagePrivate::isBeingAdopted() @@ -470,8 +509,26 @@ void QWebEnginePagePrivate::didFetchDocumentInnerText(quint64 requestId, const Q void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { +#if QT_CONFIG(webengine_printing_and_pdf) + // If no currentPrinter is set that means that were printing to PDF only. + if (!currentPrinter) { + if (!result.data()) + return; + if (auto callback = m_pdfResultCallbacks.take(requestId)) + callback(*(result.data())); + return; + } + if (view) - view->didPrintPage(requestId, result); + view->didPrintPage(currentPrinter, result); + else + currentPrinter = nullptr; +#else + // we should never enter this branch, but just for safe-keeping... + Q_UNUSED(result); + if (auto callback = m_pdfResultCallbacks.take(requestId)) + callback(QByteArray()); +#endif } bool QWebEnginePagePrivate::passOnFocus(bool reverse) @@ -503,7 +560,7 @@ void QWebEnginePagePrivate::releaseProfile() { qWarning("Release of profile requested but WebEnginePage still not deleted. Expect troubles !"); // this is not the way to go, but might avoid the crash if user code does not make any calls to page. - delete q_ptr->d_ptr.take(); + q_ptr->d_ptr.reset(); } void QWebEnginePagePrivate::showColorDialog(QSharedPointer<ColorChooserController> controller) @@ -538,6 +595,10 @@ static QWebEnginePage::Feature toFeature(QtWebEngineCore::ProfileAdapter::Permis return QWebEnginePage::Notifications; case QtWebEngineCore::ProfileAdapter::GeolocationPermission: return QWebEnginePage::Geolocation; + case QtWebEngineCore::ProfileAdapter::ClipboardReadWrite: + return QWebEnginePage::ClipboardReadWrite; + case QtWebEngineCore::ProfileAdapter::LocalFontsPermission: + return QWebEnginePage::LocalFontsAccess; default: break; } @@ -557,16 +618,25 @@ void QWebEnginePagePrivate::runMouseLockPermissionRequest(const QUrl &securityOr Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::MouseLock); } -void QWebEnginePagePrivate::runQuotaRequest(QWebEngineQuotaRequest request) +void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest request) { Q_Q(QWebEnginePage); - Q_EMIT q->quotaRequested(request); + Q_EMIT q->registerProtocolHandlerRequested(request); } -void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest request) +/*! + \fn void QWebEnginePage::fileSystemAccessRequested(QWebEngineFileSystemAccessRequest request) + \since 6.4 + + This signal is emitted when the web page requests access to local files or directories. + + The request object \a request can be used to accept or reject the request. +*/ + +void QWebEnginePagePrivate::runFileSystemAccessRequest(QWebEngineFileSystemAccessRequest request) { Q_Q(QWebEnginePage); - Q_EMIT q->registerProtocolHandlerRequested(request); + Q_EMIT q->fileSystemAccessRequested(request); } QObject *QWebEnginePagePrivate::accessibilityParentObject() @@ -576,7 +646,7 @@ QObject *QWebEnginePagePrivate::accessibilityParentObject() void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const { -#ifdef QT_NO_ACTION +#if !QT_CONFIG(action) Q_UNUSED(action); #else QAction *a = actions[action]; @@ -619,7 +689,7 @@ void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const } a->setEnabled(enabled); -#endif // QT_NO_ACTION +#endif // QT_CONFIG(action) } void QWebEnginePagePrivate::updateNavigationActions() @@ -644,7 +714,7 @@ void QWebEnginePagePrivate::updateEditActions() updateAction(QWebEnginePage::Unselect); } -#ifndef QT_NO_ACTION +#if QT_CONFIG(action) void QWebEnginePagePrivate::_q_webActionTriggered(bool checked) { Q_Q(QWebEnginePage); @@ -654,7 +724,7 @@ void QWebEnginePagePrivate::_q_webActionTriggered(bool checked) QWebEnginePage::WebAction action = static_cast<QWebEnginePage::WebAction>(a->data().toInt()); q->triggerAction(action, checked); } -#endif // QT_NO_ACTION +#endif // QT_CONFIG(action) void QWebEnginePagePrivate::recreateFromSerializedHistory(QDataStream &input) { @@ -709,12 +779,31 @@ void QWebEnginePagePrivate::findTextFinished(const QWebEngineFindTextResult &res Q_EMIT q->findTextFinished(result); } +void QWebEnginePagePrivate::showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller, + const QRect &bounds, bool autoselectFirstSuggestion) +{ + if (view) + view->showAutofillPopup(controller, bounds, autoselectFirstSuggestion); +} + +void QWebEnginePagePrivate::hideAutofillPopup() +{ + if (view) + view->hideAutofillPopup(); +} + void QWebEnginePagePrivate::ensureInitialized() const { if (!adapter->isInitialized()) adapter->loadDefault(); } +void QWebEnginePagePrivate::showWebAuthDialog(QWebEngineWebAuthUxRequest *request) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->webAuthUxRequested(request); +} + QWebEnginePage::QWebEnginePage(QObject* parent) : QObject(parent) , d_ptr(new QWebEnginePagePrivate()) @@ -778,12 +867,13 @@ QWebEnginePage::QWebEnginePage(QObject* parent) /*! \fn QWebEnginePage::quotaRequested(QWebEngineQuotaRequest quotaRequest) \since 5.11 + \deprecated [6.5] This signal is no longer emitted. - This signal is emitted when the web page requests larger persistent storage - than the application's current allocation in File System API. The default quota - is 0 bytes. + Requesting host quota is no longer supported by Chromium. + The behavior of navigator.webkitPersistentStorage + is identical to navigator.webkitTemporaryStorage. - The request object \a quotaRequest can be used to accept or reject the request. + For further details, see https://crbug.com/1233525 */ /*! @@ -828,8 +918,6 @@ QWebEnginePage::QWebEnginePage(QObject* parent) the audio is played or stopped. \note The signal is also emitted when calling the setAudioMuted() method. - Also, if the audio is paused, this signal is emitted with an approximate \b{two-second - delay}, from the moment the audio is paused. */ /*! @@ -883,9 +971,9 @@ QWebEnginePage::~QWebEnginePage() setDevToolsPage(nullptr); emit _q_aboutToDelete(); - for (auto varFun : qAsConst(d_ptr->m_variantCallbacks)) + for (auto varFun : std::as_const(d_ptr->m_variantCallbacks)) varFun(QVariant()); - for (auto strFun : qAsConst(d_ptr->m_stringCallbacks)) + for (auto strFun : std::as_const(d_ptr->m_stringCallbacks)) strFun(QString()); d_ptr->m_variantCallbacks.clear(); d_ptr->m_stringCallbacks.clear(); @@ -1076,157 +1164,16 @@ QString QWebEnginePage::selectedText() const return d->adapter->selectedText(); } -#ifndef QT_NO_ACTION +#if QT_CONFIG(action) QAction *QWebEnginePage::action(WebAction action) const { Q_D(const QWebEnginePage); if (action == QWebEnginePage::NoWebAction) - return 0; + return nullptr; if (d->actions[action]) return d->actions[action]; - QString text; - switch (action) { - case Back: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Back); - break; - case Forward: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Forward); - break; - case Stop: - text = tr("Stop"); - break; - case Reload: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Reload); - break; - case ReloadAndBypassCache: - text = tr("Reload and Bypass Cache"); - break; - case Cut: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Cut); - break; - case Copy: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Copy); - break; - case Paste: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Paste); - break; - case Undo: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Undo); - break; - case Redo: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Redo); - break; - case SelectAll: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::SelectAll); - break; - case PasteAndMatchStyle: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::PasteAndMatchStyle); - break; - case OpenLinkInThisWindow: - text = tr("Open link in this window"); - break; - case OpenLinkInNewWindow: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::OpenLinkInNewWindow); - break; - case OpenLinkInNewTab: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::OpenLinkInNewTab); - break; - case OpenLinkInNewBackgroundTab: - text = tr("Open link in new background tab"); - break; - case CopyLinkToClipboard: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyLinkToClipboard); - break; - case DownloadLinkToDisk: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadLinkToDisk); - break; - case CopyImageToClipboard: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyImageToClipboard); - break; - case CopyImageUrlToClipboard: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyImageUrlToClipboard); - break; - case DownloadImageToDisk: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadImageToDisk); - break; - case CopyMediaUrlToClipboard: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::CopyMediaUrlToClipboard); - break; - case ToggleMediaControls: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ToggleMediaControls); - break; - case ToggleMediaLoop: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ToggleMediaLoop); - break; - case ToggleMediaPlayPause: - text = tr("Toggle Play/Pause"); - break; - case ToggleMediaMute: - text = tr("Toggle Mute"); - break; - case DownloadMediaToDisk: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::DownloadMediaToDisk); - break; - case InspectElement: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::InspectElement); - break; - case ExitFullScreen: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ExitFullScreen); - break; - case RequestClose: - text = tr("Close Page"); - break; - case Unselect: - text = tr("Unselect"); - break; - case SavePage: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::SavePage); - break; - case ViewSource: - text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::ViewSource); - break; - case ToggleBold: - text = tr("&Bold"); - break; - case ToggleItalic: - text = tr("&Italic"); - break; - case ToggleUnderline: - text = tr("&Underline"); - break; - case ToggleStrikethrough: - text = tr("&Strikethrough"); - break; - case AlignLeft: - text = tr("Align &Left"); - break; - case AlignCenter: - text = tr("Align &Center"); - break; - case AlignRight: - text = tr("Align &Right"); - break; - case AlignJustified: - text = tr("Align &Justified"); - break; - case Indent: - text = tr("&Indent"); - break; - case Outdent: - text = tr("&Outdent"); - break; - case InsertOrderedList: - text = tr("Insert &Ordered List"); - break; - case InsertUnorderedList: - text = tr("Insert &Unordered List"); - break; - case NoWebAction: - case WebActionCount: - Q_UNREACHABLE(); - break; - } + const QString text = QWebEnginePagePrivate::actionText(action); QAction *a = new QAction(const_cast<QWebEnginePage*>(this)); a->setText(text); @@ -1238,7 +1185,7 @@ QAction *QWebEnginePage::action(WebAction action) const d->updateAction(action); return a; } -#endif // QT_NO_ACTION +#endif // QT_CONFIG(action) void QWebEnginePage::triggerAction(WebAction action, bool) { @@ -1285,26 +1232,26 @@ void QWebEnginePage::triggerAction(WebAction action, bool) d->adapter->unselect(); break; case OpenLinkInThisWindow: - if (d->view && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) setUrl(d->view->lastContextMenuRequest()->filteredLinkUrl()); break; case OpenLinkInNewWindow: - if (d->view && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) d->createNewWindow(WebContentsAdapterClient::NewWindowDisposition, true, d->view->lastContextMenuRequest()->filteredLinkUrl()); break; case OpenLinkInNewTab: - if (d->view && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) d->createNewWindow(WebContentsAdapterClient::NewForegroundTabDisposition, true, d->view->lastContextMenuRequest()->filteredLinkUrl()); break; case OpenLinkInNewBackgroundTab: - if (d->view && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) d->createNewWindow(WebContentsAdapterClient::NewBackgroundTabDisposition, true, d->view->lastContextMenuRequest()->filteredLinkUrl()); break; case CopyLinkToClipboard: - if (d->view && !d->view->lastContextMenuRequest()->linkUrl().isEmpty()) { + if (d->view && d->view->lastContextMenuRequest() && !d->view->lastContextMenuRequest()->linkUrl().isEmpty()) { QString urlString = d->view->lastContextMenuRequest()->linkUrl().toString( QUrl::FullyEncoded); QString linkText = d->view->lastContextMenuRequest()->linkText().toHtmlEscaped(); @@ -1321,7 +1268,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case DownloadLinkToDisk: - if (d->view && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->filteredLinkUrl().isValid()) d->adapter->download(d->view->lastContextMenuRequest()->filteredLinkUrl(), d->view->lastContextMenuRequest()->suggestedFileName(), d->view->lastContextMenuRequest()->referrerUrl(), @@ -1329,7 +1276,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) break; case CopyImageToClipboard: - if (d->view && d->view->lastContextMenuRequest()->hasImageContent() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->hasImageContent() && (d->view->lastContextMenuRequest()->mediaType() == QWebEngineContextMenuRequest::MediaTypeImage || d->view->lastContextMenuRequest()->mediaType() @@ -1338,7 +1285,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case CopyImageUrlToClipboard: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && d->view->lastContextMenuRequest()->mediaType() == QWebEngineContextMenuRequest::MediaTypeImage) { QString urlString = @@ -1359,14 +1306,14 @@ void QWebEnginePage::triggerAction(WebAction action, bool) break; case DownloadImageToDisk: case DownloadMediaToDisk: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid()) + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid()) d->adapter->download(d->view->lastContextMenuRequest()->mediaUrl(), d->view->lastContextMenuRequest()->suggestedFileName(), d->view->lastContextMenuRequest()->referrerUrl(), d->view->lastContextMenuRequest()->referrerPolicy()); break; case CopyMediaUrlToClipboard: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && (d->view->lastContextMenuRequest()->mediaType() == QWebEngineContextMenuRequest::MediaTypeAudio || d->view->lastContextMenuRequest()->mediaType() @@ -1390,7 +1337,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case ToggleMediaControls: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && d->view->lastContextMenuRequest()->mediaFlags() & QWebEngineContextMenuRequest::MediaCanToggleControls) { bool enable = !(d->view->lastContextMenuRequest()->mediaFlags() @@ -1400,7 +1347,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case ToggleMediaLoop: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && (d->view->lastContextMenuRequest()->mediaType() == QWebEngineContextMenuRequest::MediaTypeAudio || d->view->lastContextMenuRequest()->mediaType() @@ -1412,7 +1359,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case ToggleMediaPlayPause: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && (d->view->lastContextMenuRequest()->mediaType() == QWebEngineContextMenuRequest::MediaTypeAudio || d->view->lastContextMenuRequest()->mediaType() @@ -1424,7 +1371,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case ToggleMediaMute: - if (d->view && d->view->lastContextMenuRequest()->mediaUrl().isValid() + if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid() && d->view->lastContextMenuRequest()->mediaFlags() & QWebEngineContextMenuRequest::MediaHasAudio) { // Make sure to negate the value, so that toggling actually works. @@ -1435,7 +1382,7 @@ void QWebEnginePage::triggerAction(WebAction action, bool) } break; case InspectElement: - if (d->view) + if (d->view && d->view->lastContextMenuRequest()) d->adapter->inspectElementAt(d->view->lastContextMenuRequest()->position()); break; case ExitFullScreen: @@ -1495,6 +1442,12 @@ void QWebEnginePage::triggerAction(WebAction action, bool) case InsertUnorderedList: runJavaScript(QStringLiteral("document.execCommand('insertUnorderedList');"), QWebEngineScript::ApplicationWorld); break; + case ChangeTextDirectionLTR: + d->adapter->changeTextDirection(true /*left to right*/); + break; + case ChangeTextDirectionRTL: + d->adapter->changeTextDirection(false /*left to right*/); + break; case NoWebAction: break; case WebActionCount: @@ -1538,6 +1491,15 @@ bool QWebEnginePage::event(QEvent *e) return QObject::event(e); } +void QWebEnginePagePrivate::desktopMediaRequested( + QtWebEngineCore::DesktopMediaController *controller) +{ + Q_Q(QWebEnginePage); + QTimer::singleShot(0, q, [q, controller]() { + Q_EMIT q->desktopMediaRequested(QWebEngineDesktopMediaRequest(controller)); + }); +} + void QWebEnginePagePrivate::contextMenuRequested(QWebEngineContextMenuRequest *data) { if (view) @@ -1545,7 +1507,7 @@ void QWebEnginePagePrivate::contextMenuRequested(QWebEngineContextMenuRequest *d } /*! - \fn bool QWebEnginePage::navigationRequested(QWebEngineNavigationRequest &request) + \fn void QWebEnginePage::navigationRequested(QWebEngineNavigationRequest &request) \since 6.2 This signal is emitted on navigation together with the call the acceptNavigationRequest(). @@ -1554,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(); } @@ -1666,8 +1628,8 @@ void QWebEnginePagePrivate::requestGeometryChange(const QRect &geometry, const Q QObject *QWebEnginePagePrivate::dragSource() const { -#if !QT_CONFIG(draganddrop) - return view; +#if QT_CONFIG(draganddrop) + return view->accessibilityParentObject(); #else return nullptr; #endif // QT_CONFIG(draganddrop) @@ -1686,12 +1648,52 @@ void QWebEnginePagePrivate::setToolTip(const QString &toolTipText) view->setToolTip(toolTipText); } +/*! + \fn void QWebEnginePage::printRequested() + \since 5.12 + + This signal is emitted when the JavaScript \c{window.print()} method is called or the user pressed the print + button of PDF viewer plugin. + Typically, the signal handler can simply call printToPdf(). + + \sa printToPdf() +*/ + void QWebEnginePagePrivate::printRequested() { + Q_Q(QWebEnginePage); + QTimer::singleShot(0, q, [q]() { + Q_EMIT q->printRequested(); + }); if (view) view->printRequested(); } +QtWebEngineCore::TouchHandleDrawableDelegate * +QWebEnginePagePrivate::createTouchHandleDelegate(const QMap<int, QImage> &images) +{ + return view->createTouchHandleDelegate(images); +} + +void QWebEnginePagePrivate::showTouchSelectionMenu( + QtWebEngineCore::TouchSelectionMenuController *controller, const QRect &selectionBounds, + const QSize &handleSize) +{ + Q_UNUSED(handleSize); + + if (controller->buttonCount() == 1) { + controller->runContextMenu(); + return; + } + + view->showTouchSelectionMenu(controller, selectionBounds); +} + +void QWebEnginePagePrivate::hideTouchSelectionMenu() +{ + view->hideTouchSelectionMenu(); +} + void QWebEnginePagePrivate::lifecycleStateChanged(LifecycleState state) { Q_Q(QWebEnginePage); @@ -1719,7 +1721,9 @@ void QWebEnginePagePrivate::visibleChanged(bool visible) The page does not take ownership of the pointer. This interceptor is called after any interceptors on the profile, and unlike profile interceptors, only - URL requests from this page are intercepted. + URL requests from this page are intercepted. If the original request was + already blocked or redirected by the profile interceptor, it will not be + intercepted by this. To unset the request interceptor, set a \c nullptr. @@ -1750,6 +1754,13 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine case Notifications: d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AskPermission); break; + case ClipboardReadWrite: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite, + ProfileAdapter::AskPermission); + break; + case LocalFontsAccess: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::AskPermission); + break; } return; } @@ -1787,6 +1798,13 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine case Notifications: d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AllowedPermission); break; + case ClipboardReadWrite: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite, + ProfileAdapter::AllowedPermission); + break; + case LocalFontsAccess: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::AllowedPermission); + break; } } else { // if (policy == PermissionDeniedByUser) switch (feature) { @@ -1806,6 +1824,13 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine case Notifications: d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::DeniedPermission); break; + case ClipboardReadWrite: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite, + ProfileAdapter::DeniedPermission); + break; + case LocalFontsAccess: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::DeniedPermission); + break; } } } @@ -1994,8 +2019,12 @@ void QWebEnginePage::setZoomFactor(qreal factor) { Q_D(QWebEnginePage); d->defaultZoomFactor = factor; - if (d->adapter->isInitialized()) + + if (d->adapter->isInitialized()) { d->adapter->setZoomFactor(factor); + // MEMO: should reset if factor was not applied due to being invalid + d->defaultZoomFactor = zoomFactor(); + } } void QWebEnginePage::runJavaScript(const QString& scriptSource, const std::function<void(const QVariant &)> &resultCallback) @@ -2009,7 +2038,10 @@ void QWebEnginePage::runJavaScript(const QString& scriptSource, const std::funct return; } quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, QWebEngineScript::MainWorld); - d->m_variantCallbacks.insert(requestId, resultCallback); + if (requestId) + d->m_variantCallbacks.insert(requestId, resultCallback); + else if (resultCallback) + resultCallback(QVariant()); } void QWebEnginePage::runJavaScript(const QString& scriptSource, quint32 worldId, const std::function<void(const QVariant &)> &resultCallback) @@ -2024,7 +2056,10 @@ void QWebEnginePage::runJavaScript(const QString& scriptSource, quint32 worldId, } if (resultCallback) { quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, worldId); - d->m_variantCallbacks.insert(requestId, resultCallback); + if (requestId) + d->m_variantCallbacks.insert(requestId, resultCallback); + else + resultCallback(QVariant()); } else { d->adapter->runJavaScript(scriptSource, worldId); } @@ -2140,9 +2175,28 @@ void QWebEnginePage::setDevToolsPage(QWebEnginePage *devToolsPage) } } +/*! + \since 6.6 + Returns the id of the developer tools host associated with this page. + + If remote debugging is enabled (see \l{Qt WebEngine Developer Tools}), the id can be used to + build the URL to connect to the developer tool websocket: + \c{ws://localhost:<debugggin-port>/devtools/page/<id>)}. The websocket can be used to to interact + with the page using the \l{https://chromedevtools.github.io/devtools-protocol/}{DevTools + Protocol}. +*/ + +QString QWebEnginePage::devToolsId() const +{ + Q_D(const QWebEnginePage); + d->ensureInitialized(); + return d->adapter->devToolsId(); +} + ASSERT_ENUMS_MATCH(FilePickerController::Open, QWebEnginePage::FileSelectOpen) ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, QWebEnginePage::FileSelectOpenMultiple) ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, QWebEnginePage::FileSelectUploadFolder) +ASSERT_ENUMS_MATCH(FilePickerController::Save, QWebEnginePage::FileSelectSave) // TODO: remove virtuals QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) @@ -2234,6 +2288,79 @@ QSizeF QWebEnginePage::contentsSize() const */ /*! + \fn void QWebEnginePage::pdfPrintingFinished(const QString &filePath, bool success) + + This signal is emitted when printing the web page into a PDF file has + finished. + \a filePath will contain the path the file was requested to be created + at, and \a success will be \c true if the file was successfully created and + \c false otherwise. + + \sa printToPdf() +*/ + +/*! + Renders the current content of the page into a PDF document and saves it + in the location specified in \a filePath. + The page size and orientation of the produced PDF document are taken from + the values specified in \a layout, while the range of pages printed is + taken from \a ranges with the default being printing all pages. + + This method issues an asynchronous request for printing the web page into + a PDF and returns immediately. + To be informed about the result of the request, connect to the signal + pdfPrintingFinished(). + + \note The \l QWebEnginePage::Stop web action can be used to interrupt + this asynchronous operation. + + If a file already exists at the provided file path, it will be overwritten. + \sa pdfPrintingFinished() +*/ +void QWebEnginePage::printToPdf(const QString &filePath, const QPageLayout &layout, const QPageRanges &ranges) +{ +#if QT_CONFIG(webengine_printing_and_pdf) + Q_D(QWebEnginePage); + d->ensureInitialized(); + d->adapter->printToPDF(layout, ranges, filePath); +#else + Q_UNUSED(filePath); + Q_UNUSED(layout); + Q_UNUSED(ranges); +#endif +} + +/*! + Renders the current content of the page into a PDF document and returns a byte array containing the PDF data + as parameter to \a resultCallback. + The page size and orientation of the produced PDF document are taken from the values specified in \a layout, + while the range of pages printed is taken from \a ranges with the default being printing all pages. + + The \a resultCallback must take a const reference to a QByteArray as parameter. If printing was successful, this byte array + will contain the PDF data, otherwise, the byte array will be empty. + + \note The \l QWebEnginePage::Stop web action can be used to interrupt this operation. + + \warning We guarantee that the callback (\a resultCallback) 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. +*/ +void QWebEnginePage::printToPdf(const std::function<void(const QByteArray&)> &resultCallback, const QPageLayout &layout, const QPageRanges &ranges) +{ + Q_D(QWebEnginePage); +#if QT_CONFIG(webengine_printing_and_pdf) + d->ensureInitialized(); + quint64 requestId = d->adapter->printToPDFCallbackResult(layout, ranges); + d->m_pdfResultCallbacks.insert(requestId, resultCallback); +#else + Q_UNUSED(layout); + Q_UNUSED(ranges); + if (resultCallback) + resultCallback(QByteArray()); +#endif +} + +/*! \internal */ void QWebEnginePage::acceptAsNewWindow(QWebEngineNewWindowRequest &request) @@ -2248,10 +2375,10 @@ void QWebEnginePage::acceptAsNewWindow(QWebEngineNewWindowRequest &request) return; } - if (adapter) - d->adoptWebContents(adapter.data()); - else + if (!adapter) setUrl(url); + else if (!d->adoptWebContents(adapter.data())) + return; QRect geometry = request.requestedGeometry(); if (!geometry.isEmpty()) @@ -2374,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(); |