| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWebEngine module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwebenginepage.h" |
| #include "qwebenginepage_p.h" |
| |
| #include "authentication_dialog_controller.h" |
| #include "profile_adapter.h" |
| #include "certificate_error_controller.h" |
| #include "color_chooser_controller.h" |
| #include "favicon_manager.h" |
| #include "find_text_helper.h" |
| #include "file_picker_controller.h" |
| #include "javascript_dialog_controller.h" |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| #include "printer_worker.h" |
| #endif |
| #include "qwebenginecertificateerror.h" |
| #include "qwebenginefindtextresult.h" |
| #include "qwebenginefullscreenrequest.h" |
| #include "qwebenginehistory.h" |
| #include "qwebenginehistory_p.h" |
| #include "qwebenginenotification.h" |
| #include "qwebengineprofile.h" |
| #include "qwebengineprofile_p.h" |
| #include "qwebenginequotarequest.h" |
| #include "qwebengineregisterprotocolhandlerrequest.h" |
| #include "qwebenginescriptcollection_p.h" |
| #include "qwebenginesettings.h" |
| #include "qwebengineview.h" |
| #include "qwebengineview_p.h" |
| #include "user_notification_controller.h" |
| #include "render_widget_host_view_qt_delegate_widget.h" |
| #include "web_contents_adapter.h" |
| #include "web_engine_settings.h" |
| #include "qwebenginescript.h" |
| |
| #include <QAction> |
| #include <QApplication> |
| #include <QAuthenticator> |
| #include <QClipboard> |
| #if QT_CONFIG(colordialog) |
| #include <QColorDialog> |
| #endif |
| #include <QContextMenuEvent> |
| #if QT_CONFIG(filedialog) |
| #include <QFileDialog> |
| #endif |
| #include <QKeyEvent> |
| #include <QIcon> |
| #if QT_CONFIG(inputdialog) |
| #include <QInputDialog> |
| #endif |
| #include <QLayout> |
| #include <QLoggingCategory> |
| #if QT_CONFIG(menu) |
| #include <QMenu> |
| #endif |
| #if QT_CONFIG(messagebox) |
| #include <QMessageBox> |
| #endif |
| #include <QMimeData> |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| #include <QPrinter> |
| #include <QThread> |
| #endif |
| #include <QStandardPaths> |
| #include <QStyle> |
| #include <QTimer> |
| #include <QUrl> |
| |
| QT_BEGIN_NAMESPACE |
| |
| using namespace QtWebEngineCore; |
| |
| static const int MaxTooltipLength = 1024; |
| |
| static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition) |
| { |
| switch (disposition) { |
| case WebContentsAdapterClient::NewForegroundTabDisposition: |
| return QWebEnginePage::WebBrowserTab; |
| case WebContentsAdapterClient::NewBackgroundTabDisposition: |
| return QWebEnginePage::WebBrowserBackgroundTab; |
| case WebContentsAdapterClient::NewPopupDisposition: |
| return QWebEnginePage::WebDialog; |
| case WebContentsAdapterClient::NewWindowDisposition: |
| return QWebEnginePage::WebBrowserWindow; |
| default: |
| Q_UNREACHABLE(); |
| } |
| } |
| |
| QWebEnginePage::WebAction editorActionForKeyEvent(QKeyEvent* event) |
| { |
| static struct { |
| QKeySequence::StandardKey standardKey; |
| QWebEnginePage::WebAction action; |
| } editorActions[] = { |
| { QKeySequence::Cut, QWebEnginePage::Cut }, |
| { QKeySequence::Copy, QWebEnginePage::Copy }, |
| { QKeySequence::Paste, QWebEnginePage::Paste }, |
| { QKeySequence::Undo, QWebEnginePage::Undo }, |
| { QKeySequence::Redo, QWebEnginePage::Redo }, |
| { QKeySequence::SelectAll, QWebEnginePage::SelectAll }, |
| { QKeySequence::UnknownKey, QWebEnginePage::NoWebAction } |
| }; |
| for (int i = 0; editorActions[i].standardKey != QKeySequence::UnknownKey; ++i) |
| if (event == editorActions[i].standardKey) |
| return editorActions[i].action; |
| |
| return QWebEnginePage::NoWebAction; |
| } |
| |
| QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) |
| : adapter(QSharedPointer<WebContentsAdapter>::create()) |
| , history(new QWebEngineHistory(new QWebEngineHistoryPrivate(this))) |
| , profile(_profile ? _profile : QWebEngineProfile::defaultProfile()) |
| , settings(new QWebEngineSettings(profile->settings())) |
| , view(0) |
| , isLoading(false) |
| , scriptCollection(new QWebEngineScriptCollectionPrivate(profileAdapter()->userResourceController(), adapter)) |
| , m_isBeingAdopted(false) |
| , m_backgroundColor(Qt::white) |
| , fullscreenMode(false) |
| , webChannel(nullptr) |
| , webChannelWorldId(QWebEngineScript::MainWorld) |
| , defaultAudioMuted(false) |
| , defaultZoomFactor(1.0) |
| , requestInterceptor(nullptr) |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| , currentPrinter(nullptr) |
| #endif |
| { |
| memset(actions, 0, sizeof(actions)); |
| |
| qRegisterMetaType<QWebEngineQuotaRequest>(); |
| qRegisterMetaType<QWebEngineRegisterProtocolHandlerRequest>(); |
| qRegisterMetaType<QWebEngineFindTextResult>(); |
| |
| // See setVisible(). |
| wasShownTimer.setSingleShot(true); |
| QObject::connect(&wasShownTimer, &QTimer::timeout, [this](){ |
| ensureInitialized(); |
| }); |
| |
| profile->d_ptr->addWebContentsAdapterClient(this); |
| } |
| |
| QWebEnginePagePrivate::~QWebEnginePagePrivate() |
| { |
| if (requestInterceptor) |
| profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); |
| delete history; |
| delete settings; |
| profile->d_ptr->removeWebContentsAdapterClient(this); |
| } |
| |
| RenderWidgetHostViewQtDelegate *QWebEnginePagePrivate::CreateRenderWidgetHostViewQtDelegate(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 |
| // should allow interaction. |
| // 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 new RenderWidgetHostViewQtDelegateWidget(client, this->view); |
| } |
| |
| void QWebEnginePagePrivate::initializationFinished() |
| { |
| if (m_backgroundColor != Qt::white) |
| adapter->setBackgroundColor(m_backgroundColor); |
| #if QT_CONFIG(webengine_webchannel) |
| if (webChannel) |
| adapter->setWebChannel(webChannel, webChannelWorldId); |
| #endif |
| if (defaultAudioMuted != adapter->isAudioMuted()) |
| adapter->setAudioMuted(defaultAudioMuted); |
| if (!qFuzzyCompare(adapter->currentZoomFactor(), defaultZoomFactor)) |
| adapter->setZoomFactor(defaultZoomFactor); |
| if (view) |
| adapter->setVisible(view->isVisible()); |
| |
| scriptCollection.d->initializationFinished(adapter); |
| |
| m_isBeingAdopted = false; |
| } |
| |
| void QWebEnginePagePrivate::titleChanged(const QString &title) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->titleChanged(title); |
| } |
| |
| void QWebEnginePagePrivate::urlChanged() |
| { |
| Q_Q(QWebEnginePage); |
| QUrl qurl = adapter->activeUrl(); |
| if (url != qurl) { |
| url = qurl; |
| Q_EMIT q->urlChanged(qurl); |
| } |
| } |
| |
| void QWebEnginePagePrivate::iconChanged(const QUrl &url) |
| { |
| Q_Q(QWebEnginePage); |
| if (iconUrl == url) |
| return; |
| iconUrl = url; |
| Q_EMIT q->iconUrlChanged(iconUrl); |
| Q_EMIT q->iconChanged(adapter->faviconManager()->getIcon()); |
| } |
| |
| void QWebEnginePagePrivate::loadProgressChanged(int progress) |
| { |
| Q_Q(QWebEnginePage); |
| QTimer::singleShot(0, q, [q, progress] () { Q_EMIT q->loadProgress(progress); }); |
| } |
| |
| void QWebEnginePagePrivate::didUpdateTargetURL(const QUrl &hoveredUrl) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->linkHovered(hoveredUrl.toString()); |
| } |
| |
| void QWebEnginePagePrivate::selectionChanged() |
| { |
| Q_Q(QWebEnginePage); |
| QTimer::singleShot(0, q, [this, q]() { |
| updateEditActions(); |
| Q_EMIT q->selectionChanged(); |
| }); |
| } |
| |
| void QWebEnginePagePrivate::recentlyAudibleChanged(bool recentlyAudible) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->recentlyAudibleChanged(recentlyAudible); |
| } |
| |
| QRectF QWebEnginePagePrivate::viewportRect() const |
| { |
| return view ? view->rect() : QRectF(); |
| } |
| |
| QColor QWebEnginePagePrivate::backgroundColor() const |
| { |
| return m_backgroundColor; |
| } |
| |
| void QWebEnginePagePrivate::loadStarted(const QUrl &provisionalUrl, bool isErrorPage) |
| { |
| Q_UNUSED(provisionalUrl); |
| Q_Q(QWebEnginePage); |
| |
| if (isErrorPage) |
| return; |
| |
| isLoading = true; |
| m_certificateErrorControllers.clear(); |
| QTimer::singleShot(0, q, &QWebEnginePage::loadStarted); |
| } |
| |
| void QWebEnginePagePrivate::loadFinished(bool success, const QUrl &url, bool isErrorPage, int errorCode, const QString &errorDescription) |
| { |
| Q_Q(QWebEnginePage); |
| Q_UNUSED(url); |
| Q_UNUSED(errorCode); |
| Q_UNUSED(errorDescription); |
| |
| if (isErrorPage) { |
| Q_ASSERT(settings->testAttribute(QWebEngineSettings::ErrorPageEnabled)); |
| QTimer::singleShot(0, q, [q](){ |
| emit q->loadFinished(false); |
| }); |
| return; |
| } |
| |
| isLoading = false; |
| // Delay notifying failure until the error-page is done loading. |
| // Error-pages are not loaded on failures due to abort. |
| if (success || errorCode == -3 /* ERR_ABORTED*/ || !settings->testAttribute(QWebEngineSettings::ErrorPageEnabled)) { |
| QTimer::singleShot(0, q, [q, success](){ |
| emit q->loadFinished(success); |
| }); |
| } |
| } |
| |
| void QWebEnginePagePrivate::didPrintPageToPdf(const QString &filePath, bool success) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->pdfPrintingFinished(filePath, success); |
| } |
| |
| void QWebEnginePagePrivate::focusContainer() |
| { |
| if (view) { |
| view->activateWindow(); |
| view->setFocus(); |
| } |
| } |
| |
| void QWebEnginePagePrivate::unhandledKeyEvent(QKeyEvent *event) |
| { |
| if (view && view->parentWidget()) |
| QGuiApplication::sendEvent(view->parentWidget(), event); |
| } |
| |
| void QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry, const QUrl &targetUrl) |
| { |
| Q_Q(QWebEnginePage); |
| Q_UNUSED(userGesture); |
| Q_UNUSED(targetUrl); |
| |
| QWebEnginePage *newPage = q->createWindow(toWindowType(disposition)); |
| if (!newPage) |
| return; |
| |
| if (newPage->d_func() == this) { |
| // If createWindow returns /this/ we must delay the adoption. |
| Q_ASSERT(q == newPage); |
| // WebContents might be null if we just opened a new page for navigation, in that case |
| // avoid referencing newWebContents so that it is deleted and WebContentsDelegateQt::OpenURLFromTab |
| // will fall back to navigating current page. |
| if (newWebContents->webContents()) { |
| QTimer::singleShot(0, q, [this, newPage, newWebContents, initialGeometry] () { |
| adoptNewWindowImpl(newPage, newWebContents, initialGeometry); |
| }); |
| } |
| } else { |
| adoptNewWindowImpl(newPage, newWebContents, initialGeometry); |
| } |
| } |
| |
| void QWebEnginePagePrivate::adoptNewWindowImpl(QWebEnginePage *newPage, |
| const QSharedPointer<WebContentsAdapter> &newWebContents, const QRect &initialGeometry) |
| { |
| // 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 (!initialGeometry.isEmpty()) |
| emit newPage->geometryChangeRequested(initialGeometry); |
| } |
| |
| bool QWebEnginePagePrivate::isBeingAdopted() |
| { |
| return m_isBeingAdopted; |
| } |
| |
| void QWebEnginePagePrivate::close() |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->windowCloseRequested(); |
| } |
| |
| void QWebEnginePagePrivate::windowCloseRejected() |
| { |
| // Do nothing for now. |
| } |
| |
| void QWebEnginePagePrivate::didRunJavaScript(quint64 requestId, const QVariant& result) |
| { |
| m_callbacks.invoke(requestId, result); |
| } |
| |
| void QWebEnginePagePrivate::didFetchDocumentMarkup(quint64 requestId, const QString& result) |
| { |
| m_callbacks.invoke(requestId, result); |
| } |
| |
| void QWebEnginePagePrivate::didFetchDocumentInnerText(quint64 requestId, const QString& result) |
| { |
| m_callbacks.invoke(requestId, result); |
| } |
| |
| void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) |
| { |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| Q_Q(QWebEnginePage); |
| |
| // If no currentPrinter is set that means that were printing to PDF only. |
| if (!currentPrinter) { |
| if (!result.data()) |
| return; |
| m_callbacks.invoke(requestId, *(result.data())); |
| return; |
| } |
| |
| QThread *printerThread = new QThread; |
| QObject::connect(printerThread, &QThread::finished, printerThread, &QThread::deleteLater); |
| printerThread->start(); |
| |
| PrinterWorker *printerWorker = new PrinterWorker(result, currentPrinter); |
| QObject::connect(printerWorker, &PrinterWorker::resultReady, q, [requestId, this](bool success) { |
| currentPrinter = nullptr; |
| m_callbacks.invoke(requestId, success); |
| }); |
| |
| QObject::connect(printerWorker, &PrinterWorker::resultReady, printerThread, &QThread::quit); |
| QObject::connect(printerThread, &QThread::finished, printerWorker, &PrinterWorker::deleteLater); |
| |
| printerWorker->moveToThread(printerThread); |
| QMetaObject::invokeMethod(printerWorker, "print"); |
| |
| #else |
| // we should never enter this branch, but just for safe-keeping... |
| Q_UNUSED(result); |
| m_callbacks.invoke(requestId, QByteArray()); |
| #endif |
| } |
| |
| bool QWebEnginePagePrivate::passOnFocus(bool reverse) |
| { |
| if (view) |
| return view->focusNextPrevChild(!reverse); |
| return false; |
| } |
| |
| void QWebEnginePagePrivate::authenticationRequired(QSharedPointer<AuthenticationDialogController> controller) |
| { |
| Q_Q(QWebEnginePage); |
| QAuthenticator networkAuth; |
| networkAuth.setRealm(controller->realm()); |
| |
| if (controller->isProxy()) |
| Q_EMIT q->proxyAuthenticationRequired(controller->url(), &networkAuth, controller->host()); |
| else |
| Q_EMIT q->authenticationRequired(controller->url(), &networkAuth); |
| |
| // Authentication has been cancelled |
| if (networkAuth.isNull()) { |
| controller->reject(); |
| return; |
| } |
| |
| controller->accept(networkAuth.user(), networkAuth.password()); |
| } |
| |
| 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(); |
| } |
| |
| void QWebEnginePagePrivate::showColorDialog(QSharedPointer<ColorChooserController> controller) |
| { |
| #if QT_CONFIG(colordialog) |
| QColorDialog *dialog = new QColorDialog(controller.data()->initialColor(), view); |
| |
| QColorDialog::connect(dialog, SIGNAL(colorSelected(QColor)), controller.data(), SLOT(accept(QColor))); |
| QColorDialog::connect(dialog, SIGNAL(rejected()), controller.data(), SLOT(reject())); |
| |
| // Delete when done |
| QColorDialog::connect(dialog, SIGNAL(colorSelected(QColor)), dialog, SLOT(deleteLater())); |
| QColorDialog::connect(dialog, SIGNAL(rejected()), dialog, SLOT(deleteLater())); |
| |
| dialog->open(); |
| #else |
| Q_UNUSED(controller); |
| #endif |
| } |
| |
| void QWebEnginePagePrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags) |
| { |
| Q_Q(QWebEnginePage); |
| QWebEnginePage::Feature feature; |
| if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) && |
| requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture)) |
| feature = QWebEnginePage::MediaAudioVideoCapture; |
| else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)) |
| feature = QWebEnginePage::MediaAudioCapture; |
| else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture)) |
| feature = QWebEnginePage::MediaVideoCapture; |
| else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture) && |
| requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture)) |
| feature = QWebEnginePage::DesktopAudioVideoCapture; |
| else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture)) |
| feature = QWebEnginePage::DesktopVideoCapture; |
| Q_EMIT q->featurePermissionRequested(securityOrigin, feature); |
| } |
| |
| void QWebEnginePagePrivate::runGeolocationPermissionRequest(const QUrl &securityOrigin) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::Geolocation); |
| } |
| |
| void QWebEnginePagePrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::MouseLock); |
| } |
| |
| void QWebEnginePagePrivate::runQuotaRequest(QWebEngineQuotaRequest request) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->quotaRequested(request); |
| } |
| |
| void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest request) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->registerProtocolHandlerRequested(request); |
| } |
| |
| void QWebEnginePagePrivate::runUserNotificationPermissionRequest(const QUrl &securityOrigin) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::Notifications); |
| } |
| |
| QObject *QWebEnginePagePrivate::accessibilityParentObject() |
| { |
| return view; |
| } |
| |
| void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const |
| { |
| #ifdef QT_NO_ACTION |
| Q_UNUSED(action) |
| #else |
| QAction *a = actions[action]; |
| if (!a) |
| return; |
| |
| bool enabled = true; |
| |
| switch (action) { |
| case QWebEnginePage::Back: |
| enabled = adapter->canGoToOffset(-1); |
| break; |
| case QWebEnginePage::Forward: |
| enabled = adapter->canGoToOffset(1); |
| break; |
| case QWebEnginePage::Stop: |
| enabled = isLoading; |
| break; |
| case QWebEnginePage::Reload: |
| case QWebEnginePage::ReloadAndBypassCache: |
| enabled = !isLoading; |
| break; |
| case QWebEnginePage::ViewSource: |
| enabled = adapter->canViewSource(); |
| break; |
| case QWebEnginePage::Cut: |
| case QWebEnginePage::Copy: |
| case QWebEnginePage::Unselect: |
| enabled = adapter->hasFocusedFrame() && !adapter->selectedText().isEmpty(); |
| break; |
| case QWebEnginePage::Paste: |
| case QWebEnginePage::Undo: |
| case QWebEnginePage::Redo: |
| case QWebEnginePage::SelectAll: |
| case QWebEnginePage::PasteAndMatchStyle: |
| enabled = adapter->hasFocusedFrame(); |
| break; |
| default: |
| break; |
| } |
| |
| a->setEnabled(enabled); |
| #endif // QT_NO_ACTION |
| } |
| |
| void QWebEnginePagePrivate::updateNavigationActions() |
| { |
| updateAction(QWebEnginePage::Back); |
| updateAction(QWebEnginePage::Forward); |
| updateAction(QWebEnginePage::Stop); |
| updateAction(QWebEnginePage::Reload); |
| updateAction(QWebEnginePage::ReloadAndBypassCache); |
| updateAction(QWebEnginePage::ViewSource); |
| } |
| |
| void QWebEnginePagePrivate::updateEditActions() |
| { |
| updateAction(QWebEnginePage::Cut); |
| updateAction(QWebEnginePage::Copy); |
| updateAction(QWebEnginePage::Paste); |
| updateAction(QWebEnginePage::Undo); |
| updateAction(QWebEnginePage::Redo); |
| updateAction(QWebEnginePage::SelectAll); |
| updateAction(QWebEnginePage::PasteAndMatchStyle); |
| updateAction(QWebEnginePage::Unselect); |
| } |
| |
| #ifndef QT_NO_ACTION |
| void QWebEnginePagePrivate::_q_webActionTriggered(bool checked) |
| { |
| Q_Q(QWebEnginePage); |
| QAction *a = qobject_cast<QAction *>(q->sender()); |
| if (!a) |
| return; |
| QWebEnginePage::WebAction action = static_cast<QWebEnginePage::WebAction>(a->data().toInt()); |
| q->triggerAction(action, checked); |
| } |
| #endif // QT_NO_ACTION |
| |
| void QWebEnginePagePrivate::recreateFromSerializedHistory(QDataStream &input) |
| { |
| QSharedPointer<WebContentsAdapter> newWebContents = WebContentsAdapter::createFromSerializedNavigationHistory(input, this); |
| if (newWebContents) { |
| adapter = std::move(newWebContents); |
| adapter->setClient(this); |
| adapter->loadDefault(); |
| } |
| } |
| |
| void QWebEnginePagePrivate::updateScrollPosition(const QPointF &position) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->scrollPositionChanged(position); |
| } |
| |
| void QWebEnginePagePrivate::updateContentsSize(const QSizeF &size) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->contentsSizeChanged(size); |
| } |
| |
| void QWebEnginePagePrivate::setFullScreenMode(bool fullscreen) |
| { |
| if (fullscreenMode != fullscreen) { |
| fullscreenMode = fullscreen; |
| adapter->changedFullScreen(); |
| } |
| } |
| |
| ProfileAdapter* QWebEnginePagePrivate::profileAdapter() |
| { |
| return profile->d_ptr->profileAdapter(); |
| } |
| |
| WebContentsAdapter *QWebEnginePagePrivate::webContentsAdapter() |
| { |
| ensureInitialized(); |
| return adapter.data(); |
| } |
| |
| const QObject *QWebEnginePagePrivate::holdingQObject() const |
| { |
| Q_Q(const QWebEnginePage); |
| return q; |
| } |
| |
| void QWebEnginePagePrivate::widgetChanged(RenderWidgetHostViewQtDelegate *newWidgetBase) |
| { |
| Q_Q(QWebEnginePage); |
| bindPageAndWidget(q, static_cast<RenderWidgetHostViewQtDelegateWidget *>(newWidgetBase)); |
| } |
| |
| void QWebEnginePagePrivate::findTextFinished(const QWebEngineFindTextResult &result) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->findTextFinished(result); |
| } |
| |
| void QWebEnginePagePrivate::ensureInitialized() const |
| { |
| if (!adapter->isInitialized()) |
| adapter->loadDefault(); |
| } |
| |
| void QWebEnginePagePrivate::bindPageAndView(QWebEnginePage *page, QWebEngineView *view) |
| { |
| auto oldView = page ? page->d_func()->view : nullptr; |
| auto oldPage = view ? view->d_func()->page : nullptr; |
| |
| bool ownNewPage = false; |
| bool deleteOldPage = false; |
| |
| // Change pointers first. |
| |
| if (page && oldView != view) { |
| if (oldView) { |
| ownNewPage = oldView->d_func()->m_ownsPage; |
| oldView->d_func()->page = nullptr; |
| oldView->d_func()->m_ownsPage = false; |
| } |
| page->d_func()->view = view; |
| } |
| |
| if (view && oldPage != page) { |
| if (oldPage) { |
| if (oldPage->d_func()) |
| oldPage->d_func()->view = nullptr; |
| deleteOldPage = view->d_func()->m_ownsPage; |
| } |
| view->d_func()->m_ownsPage = ownNewPage; |
| view->d_func()->page = page; |
| } |
| |
| // Then notify. |
| |
| auto widget = page ? page->d_func()->widget : nullptr; |
| auto oldWidget = (oldPage && oldPage->d_func()) ? oldPage->d_func()->widget : nullptr; |
| |
| if (page && oldView != view && oldView) { |
| oldView->d_func()->pageChanged(page, nullptr); |
| if (widget) |
| oldView->d_func()->widgetChanged(widget, nullptr); |
| } |
| |
| if (view && oldPage != page) { |
| if (oldPage && oldPage->d_func()) |
| view->d_func()->pageChanged(oldPage, page); |
| else |
| view->d_func()->pageChanged(nullptr, page); |
| if (oldWidget != widget) |
| view->d_func()->widgetChanged(oldWidget, widget); |
| } |
| if (deleteOldPage) |
| delete oldPage; |
| } |
| |
| void QWebEnginePagePrivate::bindPageAndWidget(QWebEnginePage *page, RenderWidgetHostViewQtDelegateWidget *widget) |
| { |
| auto oldPage = widget ? widget->m_page : nullptr; |
| auto oldWidget = page ? page->d_func()->widget : nullptr; |
| |
| // Change pointers first. |
| |
| if (widget && oldPage != page) { |
| if (oldPage && oldPage->d_func()) |
| oldPage->d_func()->widget = nullptr; |
| widget->m_page = page; |
| } |
| |
| if (page && oldWidget != widget) { |
| if (oldWidget) |
| oldWidget->m_page = nullptr; |
| page->d_func()->widget = widget; |
| } |
| |
| // Then notify. |
| |
| if (widget && oldPage != page && oldPage && oldPage->d_func()) { |
| if (auto oldView = oldPage->d_func()->view) |
| oldView->d_func()->widgetChanged(widget, nullptr); |
| } |
| |
| if (page && oldWidget != widget) { |
| if (auto view = page->d_func()->view) |
| view->d_func()->widgetChanged(oldWidget, widget); |
| } |
| } |
| |
| QWebEnginePage::QWebEnginePage(QObject* parent) |
| : QObject(parent) |
| , d_ptr(new QWebEnginePagePrivate()) |
| { |
| Q_D(QWebEnginePage); |
| d->q_ptr = this; |
| d->adapter->setClient(d); |
| } |
| |
| /*! |
| \fn void QWebEnginePage::findTextFinished(const QWebEngineFindTextResult &result) |
| \since 5.14 |
| |
| This signal is emitted when a search string search on a page is completed. \a result is |
| the result of the string search. |
| |
| \sa findText() |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::printRequested() |
| \since 5.12 |
| |
| This signal is emitted when the JavaScript \c{window.print()} method is called. |
| Typically, the signal handler can simply call printToPdf(). |
| |
| \sa printToPdf() |
| */ |
| |
| /*! |
| \enum QWebEnginePage::RenderProcessTerminationStatus |
| \since 5.6 |
| |
| This enum describes the status with which the render process terminated: |
| |
| \value NormalTerminationStatus |
| The render process terminated normally. |
| \value AbnormalTerminationStatus |
| The render process terminated with with a non-zero exit status. |
| \value CrashedTerminationStatus |
| The render process crashed, for example because of a segmentation fault. |
| \value KilledTerminationStatus |
| The render process was killed, for example by \c SIGKILL or task manager kill. |
| */ |
| |
| /*! |
| \fn QWebEnginePage::renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) |
| \since 5.6 |
| |
| This signal is emitted when the render process is terminated with a non-zero exit status. |
| \a terminationStatus is the termination status of the process and \a exitCode is the status code |
| with which the process terminated. |
| */ |
| |
| /*! |
| \fn QWebEnginePage::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) |
| |
| This signal is emitted when the web page issues the request to enter fullscreen mode for |
| a web-element, usually a video element. |
| |
| The request object \a fullScreenRequest can be used to accept or reject the request. |
| |
| If the request is accepted the element requesting fullscreen will fill the viewport, |
| but it is up to the application to make the view fullscreen or move the page to a view |
| that is fullscreen. |
| |
| \sa QWebEngineSettings::FullScreenSupportEnabled |
| */ |
| |
| /*! |
| \fn QWebEnginePage::quotaRequested(QWebEngineQuotaRequest quotaRequest) |
| \since 5.11 |
| |
| 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. |
| |
| The request object \a quotaRequest can be used to accept or reject the request. |
| */ |
| |
| /*! |
| \fn QWebEnginePage::registerProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request) |
| \since 5.11 |
| |
| This signal is emitted when the web page tries to register a custom protocol |
| using the \l registerProtocolHandler API. |
| |
| The request object \a request can be used to accept or reject the request: |
| |
| \snippet webenginewidgets/simplebrowser/webpage.cpp registerProtocolHandlerRequested |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::pdfPrintingFinished(const QString &filePath, bool success) |
| \since 5.9 |
| |
| 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() |
| */ |
| |
| /*! |
| \property QWebEnginePage::scrollPosition |
| \since 5.7 |
| |
| \brief The scroll position of the page contents. |
| */ |
| |
| /*! |
| \property QWebEnginePage::contentsSize |
| \since 5.7 |
| |
| \brief The size of the page contents. |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::audioMutedChanged(bool muted) |
| \since 5.7 |
| |
| This signal is emitted when the page's \a muted state changes. |
| \note Not to be confused with a specific HTML5 audio or video element being muted. |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::recentlyAudibleChanged(bool recentlyAudible); |
| \since 5.7 |
| |
| This signal is emitted when the page's audible state, \a recentlyAudible, changes, because |
| 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. |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::iconUrlChanged(const QUrl &url) |
| |
| This signal is emitted when the URL of the icon ("favicon") associated with the |
| page is changed. The new URL is specified by \a url. |
| |
| \sa iconUrl(), icon(), iconChanged() |
| */ |
| |
| /*! |
| \fn void QWebEnginePage::iconChanged(const QIcon &icon) |
| \since 5.7 |
| |
| This signal is emitted when the icon ("favicon") associated with the |
| page is changed. The new icon is specified by \a icon. |
| |
| \sa icon(), iconUrl(), iconUrlChanged() |
| */ |
| |
| /*! |
| Constructs an empty web engine page in the web engine profile \a profile with the parent |
| \a parent. |
| |
| If the profile is not the default profile, the caller must ensure that the profile stays alive |
| for as long as the page does. |
| |
| \since 5.5 |
| */ |
| QWebEnginePage::QWebEnginePage(QWebEngineProfile *profile, QObject* parent) |
| : QObject(parent) |
| , d_ptr(new QWebEnginePagePrivate(profile)) |
| { |
| Q_D(QWebEnginePage); |
| d->q_ptr = this; |
| d->adapter->setClient(d); |
| } |
| |
| QWebEnginePage::~QWebEnginePage() |
| { |
| if (d_ptr) { |
| // d_ptr might be exceptionally null if profile adapter got deleted first |
| setDevToolsPage(nullptr); |
| QWebEnginePagePrivate::bindPageAndView(this, nullptr); |
| QWebEnginePagePrivate::bindPageAndWidget(this, nullptr); |
| } |
| } |
| |
| QWebEngineHistory *QWebEnginePage::history() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->history; |
| } |
| |
| QWebEngineSettings *QWebEnginePage::settings() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->settings; |
| } |
| |
| /*! |
| * Returns a pointer to the web channel instance used by this page or a null pointer if none was set. |
| * This channel automatically uses the internal web engine transport mechanism over Chromium IPC |
| * that is exposed in the JavaScript context of this page as \c qt.webChannelTransport. |
| * |
| * \since 5.5 |
| * \sa setWebChannel() |
| */ |
| QWebChannel *QWebEnginePage::webChannel() const |
| { |
| #if QT_CONFIG(webengine_webchannel) |
| Q_D(const QWebEnginePage); |
| return d->webChannel; |
| #endif |
| qWarning("WebEngine compiled without webchannel support"); |
| return nullptr; |
| } |
| |
| /*! |
| * \overload |
| * |
| * Sets the web channel instance to be used by this page to \a channel and installs |
| * it in the main JavaScript world. |
| * |
| * With this method the web channel can be accessed by web page content. If the content |
| * is not under your control and might be hostile, this could be a security issue and |
| * you should consider installing it in a private JavaScript world. |
| * |
| * \since 5.5 |
| * \sa QWebEngineScript::MainWorld |
| */ |
| |
| void QWebEnginePage::setWebChannel(QWebChannel *channel) |
| { |
| setWebChannel(channel, QWebEngineScript::MainWorld); |
| } |
| |
| /*! |
| * Sets the web channel instance to be used by this page to \a channel and connects it to |
| * web engine's transport using Chromium IPC messages. The transport is exposed in the JavaScript |
| * world \a worldId as |
| * \c qt.webChannelTransport, which should be used when using the \l{Qt WebChannel JavaScript API}. |
| * |
| * \note The page does not take ownership of the channel object. |
| * \note Only one web channel can be installed per page, setting one even in another JavaScript |
| * world uninstalls any already installed web channel. |
| * |
| * \since 5.7 |
| * \sa QWebEngineScript::ScriptWorldId |
| */ |
| void QWebEnginePage::setWebChannel(QWebChannel *channel, uint worldId) |
| { |
| #if QT_CONFIG(webengine_webchannel) |
| Q_D(QWebEnginePage); |
| if (d->webChannel != channel || d->webChannelWorldId != worldId) { |
| d->webChannel = channel; |
| d->webChannelWorldId = worldId; |
| d->adapter->setWebChannel(channel, worldId); |
| } |
| #else |
| Q_UNUSED(channel) |
| Q_UNUSED(worldId) |
| qWarning("WebEngine compiled without webchannel support"); |
| #endif |
| } |
| |
| /*! |
| \property QWebEnginePage::backgroundColor |
| \brief The page's background color behind the document's body. |
| \since 5.6 |
| |
| You can set the background color to Qt::transparent or to a translucent |
| color to see through the document, or you can set it to match your |
| web content in a hybrid application to prevent the white flashes that may appear |
| during loading. |
| |
| The default value is white. |
| */ |
| QColor QWebEnginePage::backgroundColor() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->m_backgroundColor; |
| } |
| |
| void QWebEnginePage::setBackgroundColor(const QColor &color) |
| { |
| Q_D(QWebEnginePage); |
| if (d->m_backgroundColor == color) |
| return; |
| d->m_backgroundColor = color; |
| d->adapter->setBackgroundColor(color); |
| } |
| |
| /*! |
| * Save the currently loaded web page to disk. |
| * |
| * The web page is saved to \a filePath in the specified \a{format}. |
| * |
| * This is a short cut for the following actions: |
| * \list |
| * \li Trigger the Save web action. |
| * \li Accept the next download item and set the specified file path and save format. |
| * \endlist |
| * |
| * This function issues an asynchronous download request for the web page and returns immediately. |
| * |
| * \sa QWebEngineDownloadItem::SavePageFormat |
| * \since 5.8 |
| */ |
| void QWebEnginePage::save(const QString &filePath, |
| QWebEngineDownloadItem::SavePageFormat format) const |
| { |
| Q_D(const QWebEnginePage); |
| d->ensureInitialized(); |
| d->adapter->save(filePath, format); |
| } |
| |
| /*! |
| \property QWebEnginePage::audioMuted |
| \brief Whether the current page audio is muted. |
| \since 5.7 |
| |
| The default value is \c false. |
| \sa recentlyAudible |
| */ |
| bool QWebEnginePage::isAudioMuted() const { |
| Q_D(const QWebEnginePage); |
| if (d->adapter->isInitialized()) |
| return d->adapter->isAudioMuted(); |
| return d->defaultAudioMuted; |
| } |
| |
| void QWebEnginePage::setAudioMuted(bool muted) { |
| Q_D(QWebEnginePage); |
| d->defaultAudioMuted = muted; |
| if (d->adapter->isInitialized()) |
| d->adapter->setAudioMuted(muted); |
| } |
| |
| /*! |
| \property QWebEnginePage::recentlyAudible |
| \brief The current page's \e {audible state}, that is, whether audio was recently played |
| or not. |
| \since 5.7 |
| |
| The default value is \c false. |
| \sa audioMuted |
| */ |
| bool QWebEnginePage::recentlyAudible() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->isInitialized() && d->adapter->recentlyAudible(); |
| } |
| |
| void QWebEnginePage::setView(QWidget *newViewBase) |
| { |
| QWebEnginePagePrivate::bindPageAndView(this, qobject_cast<QWebEngineView *>(newViewBase)); |
| } |
| |
| QWidget *QWebEnginePage::view() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->view; |
| } |
| |
| /*! |
| Returns the web engine profile the page belongs to. |
| \since 5.5 |
| */ |
| QWebEngineProfile *QWebEnginePage::profile() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->profile; |
| } |
| |
| bool QWebEnginePage::hasSelection() const |
| { |
| return !selectedText().isEmpty(); |
| } |
| |
| QString QWebEnginePage::selectedText() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->selectedText(); |
| } |
| |
| #ifndef QT_NO_ACTION |
| QAction *QWebEnginePage::action(WebAction action) const |
| { |
| Q_D(const QWebEnginePage); |
| if (action == QWebEnginePage::NoWebAction) |
| return 0; |
| if (d->actions[action]) |
| return d->actions[action]; |
| |
| QString text; |
| QIcon icon; |
| QStyle *style = d->view ? d->view->style() : qApp->style(); |
| |
| switch (action) { |
| case Back: |
| text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Back); |
| icon = style->standardIcon(QStyle::SP_ArrowBack); |
| break; |
| case Forward: |
| text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Forward); |
| icon = style->standardIcon(QStyle::SP_ArrowForward); |
| break; |
| case Stop: |
| text = tr("Stop"); |
| icon = style->standardIcon(QStyle::SP_BrowserStop); |
| break; |
| case Reload: |
| text = RenderViewContextMenuQt::getMenuItemName(RenderViewContextMenuQt::ContextMenuItem::Reload); |
| icon = style->standardIcon(QStyle::SP_BrowserReload); |
| break; |
| case ReloadAndBypassCache: |
| text = tr("Reload and Bypass Cache"); |
| icon = style->standardIcon(QStyle::SP_BrowserReload); |
| 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; |
| } |
| |
| QAction *a = new QAction(const_cast<QWebEnginePage*>(this)); |
| a->setText(text); |
| a->setData(action); |
| a->setIcon(icon); |
| |
| connect(a, SIGNAL(triggered(bool)), this, SLOT(_q_webActionTriggered(bool))); |
| |
| d->actions[action] = a; |
| d->updateAction(action); |
| return a; |
| } |
| #endif // QT_NO_ACTION |
| |
| void QWebEnginePage::triggerAction(WebAction action, bool) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| const QtWebEngineCore::WebEngineContextMenuData *menuData = d->contextData.d; |
| switch (action) { |
| case Back: |
| d->adapter->navigateBack(); |
| break; |
| case Forward: |
| d->adapter->navigateForward(); |
| break; |
| case Stop: |
| d->adapter->stop(); |
| break; |
| case Reload: |
| d->adapter->reload(); |
| break; |
| case ReloadAndBypassCache: |
| d->adapter->reloadAndBypassCache(); |
| break; |
| case Cut: |
| d->adapter->cut(); |
| break; |
| case Copy: |
| d->adapter->copy(); |
| break; |
| case Paste: |
| d->adapter->paste(); |
| break; |
| case Undo: |
| d->adapter->undo(); |
| break; |
| case Redo: |
| d->adapter->redo(); |
| break; |
| case SelectAll: |
| d->adapter->selectAll(); |
| break; |
| case PasteAndMatchStyle: |
| d->adapter->pasteAndMatchStyle(); |
| break; |
| case Unselect: |
| d->adapter->unselect(); |
| break; |
| case OpenLinkInThisWindow: |
| if (menuData && menuData->linkUrl().isValid()) |
| setUrl(menuData->linkUrl()); |
| break; |
| case OpenLinkInNewWindow: |
| if (menuData && menuData->linkUrl().isValid()) { |
| QWebEnginePage *newPage = createWindow(WebBrowserWindow); |
| if (newPage) |
| newPage->setUrl(menuData->linkUrl()); |
| } |
| break; |
| case OpenLinkInNewTab: |
| if (menuData && menuData->linkUrl().isValid()) { |
| QWebEnginePage *newPage = createWindow(WebBrowserTab); |
| if (newPage) |
| newPage->setUrl(menuData->linkUrl()); |
| } |
| break; |
| case OpenLinkInNewBackgroundTab: |
| if (menuData && menuData->linkUrl().isValid()) { |
| QWebEnginePage *newPage = createWindow(WebBrowserBackgroundTab); |
| if (newPage) |
| newPage->setUrl(menuData->linkUrl()); |
| } |
| break; |
| case CopyLinkToClipboard: |
| if (menuData && !menuData->unfilteredLinkUrl().isEmpty()) { |
| QString urlString = menuData->unfilteredLinkUrl().toString(QUrl::FullyEncoded); |
| QString linkText = menuData->linkText().toHtmlEscaped(); |
| QString title = menuData->titleText(); |
| if (!title.isEmpty()) |
| title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped()); |
| QMimeData *data = new QMimeData(); |
| data->setText(urlString); |
| QString html = QStringLiteral("<a href=\"") + urlString + QStringLiteral("\"") + title + QStringLiteral(">") |
| + linkText + QStringLiteral("</a>"); |
| data->setHtml(html); |
| data->setUrls(QList<QUrl>() << menuData->unfilteredLinkUrl()); |
| qApp->clipboard()->setMimeData(data); |
| } |
| break; |
| case DownloadLinkToDisk: |
| if (menuData && menuData->linkUrl().isValid()) |
| d->adapter->download(menuData->linkUrl(), menuData->suggestedFileName(), |
| menuData->referrerUrl(), menuData->referrerPolicy()); |
| |
| break; |
| case CopyImageToClipboard: |
| if (menuData && menuData->hasImageContent() && |
| (menuData->mediaType() == WebEngineContextMenuData::MediaTypeImage || |
| menuData->mediaType() == WebEngineContextMenuData::MediaTypeCanvas)) |
| { |
| d->adapter->copyImageAt(menuData->position()); |
| } |
| break; |
| case CopyImageUrlToClipboard: |
| if (menuData && menuData->mediaUrl().isValid() && menuData->mediaType() == WebEngineContextMenuData::MediaTypeImage) { |
| QString urlString = menuData->mediaUrl().toString(QUrl::FullyEncoded); |
| QString alt = menuData->altText(); |
| if (!alt.isEmpty()) |
| alt = QStringLiteral(" alt=\"%1\"").arg(alt.toHtmlEscaped()); |
| QString title = menuData->titleText(); |
| if (!title.isEmpty()) |
| title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped()); |
| QMimeData *data = new QMimeData(); |
| data->setText(urlString); |
| QString html = QStringLiteral("<img src=\"") + urlString + QStringLiteral("\"") + title + alt + QStringLiteral("></img>"); |
| data->setHtml(html); |
| data->setUrls(QList<QUrl>() << menuData->mediaUrl()); |
| qApp->clipboard()->setMimeData(data); |
| } |
| break; |
| case DownloadImageToDisk: |
| case DownloadMediaToDisk: |
| if (menuData && menuData->mediaUrl().isValid()) |
| d->adapter->download(menuData->mediaUrl(), menuData->suggestedFileName(), |
| menuData->referrerUrl(), menuData->referrerPolicy()); |
| break; |
| case CopyMediaUrlToClipboard: |
| if (menuData && menuData->mediaUrl().isValid() && |
| (menuData->mediaType() == WebEngineContextMenuData::MediaTypeAudio || |
| menuData->mediaType() == WebEngineContextMenuData::MediaTypeVideo)) |
| { |
| QString urlString = menuData->mediaUrl().toString(QUrl::FullyEncoded); |
| QString title = menuData->titleText(); |
| if (!title.isEmpty()) |
| title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped()); |
| QMimeData *data = new QMimeData(); |
| data->setText(urlString); |
| if (menuData->mediaType() == WebEngineContextMenuData::MediaTypeAudio) |
| data->setHtml(QStringLiteral("<audio src=\"") + urlString + QStringLiteral("\"") + title + |
| QStringLiteral("></audio>")); |
| else |
| data->setHtml(QStringLiteral("<video src=\"") + urlString + QStringLiteral("\"") + title + |
| QStringLiteral("></video>")); |
| data->setUrls(QList<QUrl>() << menuData->mediaUrl()); |
| qApp->clipboard()->setMimeData(data); |
| } |
| break; |
| case ToggleMediaControls: |
| if (menuData && menuData->mediaUrl().isValid() && menuData->mediaFlags() & WebEngineContextMenuData::MediaCanToggleControls) { |
| bool enable = !(menuData->mediaFlags() & WebEngineContextMenuData::MediaControls); |
| d->adapter->executeMediaPlayerActionAt(menuData->position(), WebContentsAdapter::MediaPlayerControls, enable); |
| } |
| break; |
| case ToggleMediaLoop: |
| if (menuData && menuData->mediaUrl().isValid() && |
| (menuData->mediaType() == WebEngineContextMenuData::MediaTypeAudio || |
| menuData->mediaType() == WebEngineContextMenuData::MediaTypeVideo)) |
| { |
| bool enable = !(menuData->mediaFlags() & WebEngineContextMenuData::MediaLoop); |
| d->adapter->executeMediaPlayerActionAt(menuData->position(), WebContentsAdapter::MediaPlayerLoop, enable); |
| } |
| break; |
| case ToggleMediaPlayPause: |
| if (menuData && menuData->mediaUrl().isValid() && |
| (menuData->mediaType() == WebEngineContextMenuData::MediaTypeAudio || |
| menuData->mediaType() == WebEngineContextMenuData::MediaTypeVideo)) |
| { |
| bool enable = (menuData->mediaFlags() & WebEngineContextMenuData::MediaPaused); |
| d->adapter->executeMediaPlayerActionAt(menuData->position(), WebContentsAdapter::MediaPlayerPlay, enable); |
| } |
| break; |
| case ToggleMediaMute: |
| if (menuData && menuData->mediaUrl().isValid() && menuData->mediaFlags() & WebEngineContextMenuData::MediaHasAudio) { |
| // Make sure to negate the value, so that toggling actually works. |
| bool enable = !(menuData->mediaFlags() & WebEngineContextMenuData::MediaMuted); |
| d->adapter->executeMediaPlayerActionAt(menuData->position(), WebContentsAdapter::MediaPlayerMute, enable); |
| } |
| break; |
| case InspectElement: |
| if (menuData) |
| d->adapter->inspectElementAt(menuData->position()); |
| break; |
| case ExitFullScreen: |
| // See under ViewSource, anything that can trigger a delete of the current view is dangerous to call directly here. |
| QTimer::singleShot(0, this, [d](){ d->adapter->exitFullScreen(); }); |
| break; |
| case RequestClose: |
| d->adapter->requestClose(); |
| break; |
| case SavePage: |
| d->adapter->save(); |
| break; |
| case ViewSource: |
| // This is a workaround to make the ViewSource action working in a context menu. |
| // The WebContentsAdapter::viewSource() method deletes a |
| // RenderWidgetHostViewQtDelegateWidget instance which passes the control to the event |
| // loop. If the QMenu::aboutToHide() signal is connected to the QObject::deleteLater() |
| // slot the QMenu is deleted by the event handler while the ViewSource action is still not |
| // completed. This may lead to a crash. To avoid this the WebContentsAdapter::viewSource() |
| // method is called indirectly via the QTimer::singleShot() function which schedules the |
| // the viewSource() call after the QMenu's destruction. |
| QTimer::singleShot(0, this, [d](){ d->adapter->viewSource(); }); |
| break; |
| case ToggleBold: |
| runJavaScript(QStringLiteral("document.execCommand('bold');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case ToggleItalic: |
| runJavaScript(QStringLiteral("document.execCommand('italic');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case ToggleUnderline: |
| runJavaScript(QStringLiteral("document.execCommand('underline');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case ToggleStrikethrough: |
| runJavaScript(QStringLiteral("document.execCommand('strikethrough');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case AlignLeft: |
| runJavaScript(QStringLiteral("document.execCommand('justifyLeft');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case AlignCenter: |
| runJavaScript(QStringLiteral("document.execCommand('justifyCenter');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case AlignRight: |
| runJavaScript(QStringLiteral("document.execCommand('justifyRight');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case AlignJustified: |
| runJavaScript(QStringLiteral("document.execCommand('justifyFull');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case Indent: |
| runJavaScript(QStringLiteral("document.execCommand('indent');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case Outdent: |
| runJavaScript(QStringLiteral("document.execCommand('outdent');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case InsertOrderedList: |
| runJavaScript(QStringLiteral("document.execCommand('insertOrderedList');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case InsertUnorderedList: |
| runJavaScript(QStringLiteral("document.execCommand('insertUnorderedList');"), QWebEngineScript::ApplicationWorld); |
| break; |
| case NoWebAction: |
| break; |
| case WebActionCount: |
| Q_UNREACHABLE(); |
| break; |
| } |
| } |
| |
| /*! |
| * \since 5.8 |
| * Replace the current misspelled word with \a replacement. |
| * |
| * The current misspelled word can be found in QWebEngineContextMenuData::misspelledWord(), |
| * and suggested replacements in QWebEngineContextMenuData::spellCheckerSuggestions(). |
| * |
| * \sa contextMenuData(), |
| */ |
| |
| void QWebEnginePage::replaceMisspelledWord(const QString &replacement) |
| { |
| Q_D(QWebEnginePage); |
| d->adapter->replaceMisspelling(replacement); |
| } |
| |
| void QWebEnginePage::findText(const QString &subString, FindFlags options, const QWebEngineCallback<bool> &resultCallback) |
| { |
| Q_D(QWebEnginePage); |
| if (!d->adapter->isInitialized()) { |
| QtWebEngineCore::CallbackDirectory().invokeEmpty(resultCallback); |
| return; |
| } |
| |
| d->adapter->findTextHelper()->startFinding(subString, options & FindCaseSensitively, options & FindBackward, resultCallback); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| bool QWebEnginePage::event(QEvent *e) |
| { |
| return QObject::event(e); |
| } |
| |
| void QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData &data) |
| { |
| #if QT_CONFIG(action) |
| if (!view) |
| return; |
| |
| contextData.reset(); |
| switch (view->contextMenuPolicy()) { |
| case Qt::DefaultContextMenu: |
| { |
| contextData = data; |
| QContextMenuEvent event(QContextMenuEvent::Mouse, data.position(), view->mapToGlobal(data.position())); |
| view->contextMenuEvent(&event); |
| return; |
| } |
| case Qt::CustomContextMenu: |
| contextData = data; |
| Q_EMIT view->customContextMenuRequested(data.position()); |
| return; |
| case Qt::ActionsContextMenu: |
| if (view->actions().count()) { |
| QContextMenuEvent event(QContextMenuEvent::Mouse, data.position(), view->mapToGlobal(data.position())); |
| QMenu::exec(view->actions(), event.globalPos(), 0, view); |
| } |
| return; |
| case Qt::PreventContextMenu: |
| case Qt::NoContextMenu: |
| return; |
| } |
| |
| Q_UNREACHABLE(); |
| #else |
| Q_UNUSED(data); |
| #endif // QT_CONFIG(action) |
| } |
| |
| void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl &url, int &navigationRequestAction, bool isMainFrame) |
| { |
| Q_Q(QWebEnginePage); |
| bool accepted = q->acceptNavigationRequest(url, static_cast<QWebEnginePage::NavigationType>(navigationType), isMainFrame); |
| if (accepted && adapter->findTextHelper()->isFindTextInProgress()) |
| adapter->findTextHelper()->stopFinding(); |
| navigationRequestAction = accepted ? WebContentsAdapterClient::AcceptRequest : WebContentsAdapterClient::IgnoreRequest; |
| } |
| |
| void QWebEnginePagePrivate::requestFullScreenMode(const QUrl &origin, bool fullscreen) |
| { |
| Q_Q(QWebEnginePage); |
| QWebEngineFullScreenRequest request(q, origin, fullscreen); |
| Q_EMIT q->fullScreenRequested(request); |
| } |
| |
| bool QWebEnginePagePrivate::isFullScreenMode() const |
| { |
| return fullscreenMode; |
| } |
| |
| void QWebEnginePagePrivate::javascriptDialog(QSharedPointer<JavaScriptDialogController> controller) |
| { |
| Q_Q(QWebEnginePage); |
| bool accepted = false; |
| QString promptResult; |
| switch (controller->type()) { |
| case AlertDialog: |
| q->javaScriptAlert(controller->securityOrigin(), controller->message()); |
| accepted = true; |
| break; |
| case ConfirmDialog: |
| accepted = q->javaScriptConfirm(controller->securityOrigin(), controller->message()); |
| break; |
| case PromptDialog: |
| accepted = q->javaScriptPrompt(controller->securityOrigin(), controller->message(), controller->defaultPrompt(), &promptResult); |
| if (accepted) |
| controller->textProvided(promptResult); |
| break; |
| case UnloadDialog: |
| accepted = q->javaScriptConfirm(controller->securityOrigin(), QCoreApplication::translate("QWebEnginePage", "Are you sure you want to leave this page? Changes that you made may not be saved.")); |
| break; |
| case InternalAuthorizationDialog: |
| #if QT_CONFIG(messagebox) |
| accepted = (QMessageBox::question(view, controller->title(), controller->message(), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes); |
| #endif // QT_CONFIG(messagebox) |
| break; |
| } |
| if (accepted) |
| controller->accept(); |
| else |
| controller->reject(); |
| } |
| |
| void QWebEnginePagePrivate::allowCertificateError(const QSharedPointer<CertificateErrorController> &controller) |
| { |
| Q_Q(QWebEnginePage); |
| bool accepted = false; |
| |
| QWebEngineCertificateError error(controller); |
| accepted = q->certificateError(error); |
| if (error.deferred() && !error.answered()) |
| m_certificateErrorControllers.append(controller); |
| else if (!error.answered()) |
| controller->accept(error.isOverridable() && accepted); |
| } |
| |
| void QWebEnginePagePrivate::selectClientCert(const QSharedPointer<ClientCertSelectController> &controller) |
| { |
| #if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) |
| Q_Q(QWebEnginePage); |
| QWebEngineClientCertificateSelection certSelection(controller); |
| |
| Q_EMIT q->selectClientCertificate(certSelection); |
| #else |
| Q_UNUSED(controller); |
| #endif |
| } |
| |
| #if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) |
| /*! |
| \fn void QWebEnginePage::selectClientCertificate(QWebEngineClientCertificateSelection clientCertificateSelection) |
| \since 5.12 |
| |
| This signal is emitted when a web site requests an SSL client certificate, and one or more were |
| found in system's client certificate store. |
| |
| Handling the signal is asynchronous, and loading will be waiting until a certificate is selected, |
| or the last copy of \a clientCertificateSelection is destroyed. |
| |
| If the signal is not handled, \a clientCertificateSelection is automatically destroyed, and loading |
| will continue without a client certificate. |
| |
| \sa QWebEngineClientCertificateSelection |
| */ |
| #endif |
| |
| void QWebEnginePagePrivate::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) |
| { |
| Q_Q(QWebEnginePage); |
| q->javaScriptConsoleMessage(static_cast<QWebEnginePage::JavaScriptConsoleMessageLevel>(level), message, lineNumber, sourceID); |
| } |
| |
| void QWebEnginePagePrivate::renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, |
| int exitCode) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->renderProcessTerminated(static_cast<QWebEnginePage::RenderProcessTerminationStatus>( |
| terminationStatus), exitCode); |
| } |
| |
| void QWebEnginePagePrivate::requestGeometryChange(const QRect &geometry, const QRect &frameGeometry) |
| { |
| Q_UNUSED(geometry); |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->geometryChangeRequested(frameGeometry); |
| } |
| |
| void QWebEnginePagePrivate::startDragging(const content::DropData &dropData, |
| Qt::DropActions allowedActions, const QPixmap &pixmap, |
| const QPoint &offset) |
| { |
| #if !QT_CONFIG(draganddrop) |
| Q_UNUSED(dropData); |
| Q_UNUSED(allowedActions); |
| Q_UNUSED(pixmap); |
| Q_UNUSED(offset); |
| #else |
| adapter->startDragging(view, dropData, allowedActions, pixmap, offset); |
| #endif // QT_CONFIG(draganddrop) |
| } |
| |
| bool QWebEnginePagePrivate::supportsDragging() const |
| { |
| return true; |
| } |
| |
| bool QWebEnginePagePrivate::isEnabled() const |
| { |
| const Q_Q(QWebEnginePage); |
| const QWidget *view = q->view(); |
| if (view) |
| return view->isEnabled(); |
| return true; |
| } |
| |
| void QWebEnginePagePrivate::setToolTip(const QString &toolTipText) |
| { |
| if (!view) |
| return; |
| |
| // Hide tooltip if shown. |
| if (toolTipText.isEmpty()) { |
| if (!view->toolTip().isEmpty()) |
| view->setToolTip(QString()); |
| |
| return; |
| } |
| |
| // Update tooltip if text was changed. |
| QString wrappedTip = QLatin1String("<p style=\"white-space:pre-wrap\">") |
| % toolTipText.toHtmlEscaped().left(MaxTooltipLength) |
| % QLatin1String("</p>"); |
| if (view->toolTip() != wrappedTip) |
| view->setToolTip(wrappedTip); |
| } |
| |
| void QWebEnginePagePrivate::printRequested() |
| { |
| Q_Q(QWebEnginePage); |
| QTimer::singleShot(0, q, [q](){ |
| Q_EMIT q->printRequested(); |
| }); |
| } |
| |
| void QWebEnginePagePrivate::lifecycleStateChanged(LifecycleState state) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->lifecycleStateChanged(static_cast<QWebEnginePage::LifecycleState>(state)); |
| } |
| |
| void QWebEnginePagePrivate::recommendedStateChanged(LifecycleState state) |
| { |
| Q_Q(QWebEnginePage); |
| QTimer::singleShot(0, q, [q, state]() { |
| Q_EMIT q->recommendedStateChanged(static_cast<QWebEnginePage::LifecycleState>(state)); |
| }); |
| } |
| |
| void QWebEnginePagePrivate::visibleChanged(bool visible) |
| { |
| Q_Q(QWebEnginePage); |
| Q_EMIT q->visibleChanged(visible); |
| } |
| |
| /*! |
| \since 5.13 |
| |
| Registers the request interceptor \a interceptor to intercept URL requests. |
| |
| The page does not take ownership of the pointer. This interceptor is called |
| after any interceptors on the profile, and unlike profile interceptors, is run |
| on the UI thread, making it thread-safer. Only URL requests from this page are |
| intercepted. |
| |
| To unset the request interceptor, set a \c nullptr. |
| |
| \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() |
| */ |
| |
| void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) |
| { |
| Q_D(QWebEnginePage); |
| bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); |
| d->requestInterceptor = interceptor; |
| if (hadInterceptorChanged) { |
| if (interceptor) |
| d->profile->d_ptr->profileAdapter()->addPageRequestInterceptor(); |
| else |
| d->profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); |
| } |
| } |
| |
| void QWebEnginePagePrivate::interceptRequest(QWebEngineUrlRequestInfo &info) |
| { |
| if (requestInterceptor) |
| requestInterceptor->interceptRequest(info); |
| } |
| |
| #if QT_CONFIG(menu) |
| QMenu *QWebEnginePage::createStandardContextMenu() |
| { |
| Q_D(QWebEnginePage); |
| if (!d->contextData.d) |
| return nullptr; |
| d->ensureInitialized(); |
| |
| QMenu *menu = new QMenu(d->view); |
| const WebEngineContextMenuData &contextMenuData = *d->contextData.d; |
| |
| QContextMenuBuilder contextMenuBuilder(contextMenuData, this, menu); |
| |
| contextMenuBuilder.initMenu(); |
| |
| menu->setAttribute(Qt::WA_DeleteOnClose, true); |
| |
| return menu; |
| } |
| #endif // QT_CONFIG(menu) |
| |
| void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEnginePage::Feature feature, QWebEnginePage::PermissionPolicy policy) |
| { |
| Q_D(QWebEnginePage); |
| if (policy == PermissionUnknown) |
| return; |
| |
| const WebContentsAdapterClient::MediaRequestFlags audioVideoCaptureFlags( |
| WebContentsAdapterClient::MediaVideoCapture | |
| WebContentsAdapterClient::MediaAudioCapture); |
| const WebContentsAdapterClient::MediaRequestFlags desktopAudioVideoCaptureFlags( |
| WebContentsAdapterClient::MediaDesktopVideoCapture | |
| WebContentsAdapterClient::MediaDesktopAudioCapture); |
| |
| if (policy == PermissionGrantedByUser) { |
| switch (feature) { |
| case MediaAudioVideoCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, audioVideoCaptureFlags); |
| break; |
| case MediaAudioCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaAudioCapture); |
| break; |
| case MediaVideoCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaVideoCapture); |
| break; |
| case DesktopAudioVideoCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, desktopAudioVideoCaptureFlags); |
| break; |
| case DesktopVideoCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaDesktopVideoCapture); |
| break; |
| case Geolocation: |
| d->adapter->runGeolocationRequestCallback(securityOrigin, true); |
| break; |
| case MouseLock: |
| d->adapter->grantMouseLockPermission(true); |
| break; |
| case Notifications: |
| d->adapter->runUserNotificationRequestCallback(securityOrigin, true); |
| break; |
| } |
| } else { // if (policy == PermissionDeniedByUser) |
| switch (feature) { |
| case MediaAudioVideoCapture: |
| case MediaAudioCapture: |
| case MediaVideoCapture: |
| case DesktopAudioVideoCapture: |
| case DesktopVideoCapture: |
| d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaNone); |
| break; |
| case Geolocation: |
| d->adapter->runGeolocationRequestCallback(securityOrigin, false); |
| break; |
| case MouseLock: |
| d->adapter->grantMouseLockPermission(false); |
| break; |
| case Notifications: |
| d->adapter->runUserNotificationRequestCallback(securityOrigin, false); |
| break; |
| } |
| } |
| } |
| |
| static inline QWebEnginePage::FileSelectionMode toPublic(FilePickerController::FileChooserMode mode) |
| { |
| // Should the underlying values change, we'll need a switch here. |
| return static_cast<QWebEnginePage::FileSelectionMode>(mode); |
| } |
| |
| void QWebEnginePagePrivate::runFileChooser(QSharedPointer<FilePickerController> controller) |
| { |
| Q_Q(QWebEnginePage); |
| |
| QStringList selectedFileNames = q->chooseFiles(toPublic(controller->mode()), (QStringList() << controller->defaultFileName()), controller->acceptedMimeTypes()); |
| |
| if (!selectedFileNames.empty()) |
| controller->accepted(selectedFileNames); |
| else |
| controller->rejected(); |
| } |
| |
| WebEngineSettings *QWebEnginePagePrivate::webEngineSettings() const |
| { |
| return settings->d_func(); |
| } |
| |
| /*! |
| \since 5.10 |
| Downloads the resource from the location given by \a url to a local file. |
| |
| If \a filename is given, it is used as the suggested file name. |
| If it is relative, the file is saved in the standard download location with |
| the given name. |
| If it is a null or empty QString, the default file name is used. |
| |
| This will emit QWebEngineProfile::downloadRequested() after the download |
| has started. |
| */ |
| |
| void QWebEnginePage::download(const QUrl& url, const QString& filename) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| d->adapter->download(url, filename); |
| } |
| |
| void QWebEnginePage::load(const QUrl& url) |
| { |
| Q_D(QWebEnginePage); |
| d->adapter->load(url); |
| } |
| |
| /*! |
| \since 5.9 |
| Issues the specified \a request and loads the response. |
| |
| \sa load(), setUrl(), url(), urlChanged(), QUrl::fromUserInput() |
| */ |
| void QWebEnginePage::load(const QWebEngineHttpRequest& request) |
| { |
| Q_D(QWebEnginePage); |
| d->adapter->load(request); |
| } |
| |
| void QWebEnginePage::toHtml(const QWebEngineCallback<const QString &> &resultCallback) const |
| { |
| Q_D(const QWebEnginePage); |
| d->ensureInitialized(); |
| quint64 requestId = d->adapter->fetchDocumentMarkup(); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| } |
| |
| void QWebEnginePage::toPlainText(const QWebEngineCallback<const QString &> &resultCallback) const |
| { |
| Q_D(const QWebEnginePage); |
| d->ensureInitialized(); |
| quint64 requestId = d->adapter->fetchDocumentInnerText(); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| } |
| |
| void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl) |
| { |
| setContent(html.toUtf8(), QStringLiteral("text/html;charset=UTF-8"), baseUrl); |
| } |
| |
| void QWebEnginePage::setContent(const QByteArray &data, const QString &mimeType, const QUrl &baseUrl) |
| { |
| Q_D(QWebEnginePage); |
| d->adapter->setContent(data, mimeType, baseUrl); |
| } |
| |
| QString QWebEnginePage::title() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->pageTitle(); |
| } |
| |
| void QWebEnginePage::setUrl(const QUrl &url) |
| { |
| Q_D(QWebEnginePage); |
| if (d->url != url) { |
| d->url = url; |
| emit urlChanged(url); |
| } |
| load(url); |
| } |
| |
| QUrl QWebEnginePage::url() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->url; |
| } |
| |
| QUrl QWebEnginePage::requestedUrl() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->requestedUrl(); |
| } |
| |
| /*! |
| \property QWebEnginePage::iconUrl |
| \brief The URL of the icon associated with the page currently viewed. |
| |
| By default, this property contains an empty URL. |
| |
| \sa iconUrlChanged(), icon(), iconChanged() |
| */ |
| QUrl QWebEnginePage::iconUrl() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->iconUrl; |
| } |
| |
| /*! |
| \property QWebEnginePage::icon |
| \brief The icon associated with the page currently viewed. |
| \since 5.7 |
| |
| By default, this property contains a null icon. If the web page specifies more than one icon, |
| the \c{icon} property encapsulates the available candidate icons in a single, |
| scalable \c{QIcon}. |
| |
| \sa iconChanged(), iconUrl(), iconUrlChanged() |
| */ |
| QIcon QWebEnginePage::icon() const |
| { |
| Q_D(const QWebEnginePage); |
| |
| if (d->iconUrl.isEmpty() || !d->adapter->isInitialized()) |
| return QIcon(); |
| |
| return d->adapter->faviconManager()->getIcon(); |
| } |
| |
| qreal QWebEnginePage::zoomFactor() const |
| { |
| Q_D(const QWebEnginePage); |
| if (d->adapter->isInitialized()) |
| return d->adapter->currentZoomFactor(); |
| return d->defaultZoomFactor; |
| } |
| |
| void QWebEnginePage::setZoomFactor(qreal factor) |
| { |
| Q_D(QWebEnginePage); |
| d->defaultZoomFactor = factor; |
| if (d->adapter->isInitialized()) |
| d->adapter->setZoomFactor(factor); |
| } |
| |
| void QWebEnginePage::runJavaScript(const QString &scriptSource) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| if (d->adapter->lifecycleState() == WebContentsAdapter::LifecycleState::Discarded) { |
| qWarning("runJavaScript: disabled in Discarded state"); |
| return; |
| } |
| d->adapter->runJavaScript(scriptSource, QWebEngineScript::MainWorld); |
| } |
| |
| void QWebEnginePage::runJavaScript(const QString& scriptSource, const QWebEngineCallback<const QVariant &> &resultCallback) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| if (d->adapter->lifecycleState() == WebContentsAdapter::LifecycleState::Discarded) { |
| qWarning("runJavaScript: disabled in Discarded state"); |
| d->m_callbacks.invokeEmpty(resultCallback); |
| return; |
| } |
| quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, QWebEngineScript::MainWorld); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| } |
| |
| void QWebEnginePage::runJavaScript(const QString &scriptSource, quint32 worldId) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| d->adapter->runJavaScript(scriptSource, worldId); |
| } |
| |
| void QWebEnginePage::runJavaScript(const QString& scriptSource, quint32 worldId, const QWebEngineCallback<const QVariant &> &resultCallback) |
| { |
| Q_D(QWebEnginePage); |
| d->ensureInitialized(); |
| quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource, worldId); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| } |
| |
| /*! |
| Returns the collection of scripts that are injected into the page. |
| |
| In addition, a page might also execute scripts |
| added through QWebEngineProfile::scripts(). |
| |
| \sa QWebEngineScriptCollection, QWebEngineScript, {Script Injection} |
| */ |
| |
| QWebEngineScriptCollection &QWebEnginePage::scripts() |
| { |
| Q_D(QWebEnginePage); |
| return d->scriptCollection; |
| } |
| |
| QWebEnginePage *QWebEnginePage::createWindow(WebWindowType type) |
| { |
| Q_D(QWebEnginePage); |
| if (d->view) { |
| QWebEngineView *newView = d->view->createWindow(type); |
| if (newView) |
| return newView->page(); |
| } |
| return 0; |
| } |
| |
| /*! |
| \since 5.11 |
| Returns the page this page is inspecting, if any. |
| |
| Returns \c nullptr if this page is not a developer tools page. |
| |
| \sa setInspectedPage(), devToolsPage() |
| */ |
| |
| QWebEnginePage *QWebEnginePage::inspectedPage() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->inspectedPage; |
| } |
| |
| /*! |
| \since 5.11 |
| Navigates this page to an internal URL that is the developer |
| tools of \a page. |
| |
| This is the same as calling setDevToolsPage() on \a page |
| with \c this as argument. |
| |
| \sa inspectedPage(), setDevToolsPage() |
| */ |
| |
| void QWebEnginePage::setInspectedPage(QWebEnginePage *page) |
| { |
| Q_D(QWebEnginePage); |
| if (d->inspectedPage == page) |
| return; |
| QWebEnginePage *oldPage = d->inspectedPage; |
| d->inspectedPage = nullptr; |
| if (oldPage) |
| oldPage->setDevToolsPage(nullptr); |
| d->inspectedPage = page; |
| if (page) |
| page->setDevToolsPage(this); |
| } |
| |
| /*! |
| \since 5.11 |
| Returns the page that is hosting the developer tools |
| of this page, if any. |
| |
| Returns \c nullptr if no developer tools page is set. |
| |
| \sa setDevToolsPage(), inspectedPage() |
| */ |
| |
| QWebEnginePage *QWebEnginePage::devToolsPage() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->devToolsPage; |
| } |
| |
| /*! |
| \since 5.11 |
| Binds \a devToolsPage to be the developer tools of this page. |
| Triggers \a devToolsPage to navigate to an internal URL |
| with the developer tools. |
| |
| This is the same as calling setInspectedPage() on \a devToolsPage |
| with \c this as argument. |
| |
| \sa devToolsPage(), setInspectedPage() |
| */ |
| |
| void QWebEnginePage::setDevToolsPage(QWebEnginePage *devToolsPage) |
| { |
| Q_D(QWebEnginePage); |
| if (d->devToolsPage == devToolsPage) |
| return; |
| d->ensureInitialized(); |
| QWebEnginePage *oldDevTools = d->devToolsPage; |
| d->devToolsPage = nullptr; |
| if (oldDevTools) |
| oldDevTools->setInspectedPage(nullptr); |
| d->devToolsPage = devToolsPage; |
| if (devToolsPage) |
| devToolsPage->setInspectedPage(this); |
| if (d->adapter) { |
| if (devToolsPage) |
| d->adapter->openDevToolsFrontend(devToolsPage->d_ptr->adapter); |
| else |
| d->adapter->closeDevToolsFrontend(); |
| } |
| } |
| |
| ASSERT_ENUMS_MATCH(FilePickerController::Open, QWebEnginePage::FileSelectOpen) |
| ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, QWebEnginePage::FileSelectOpenMultiple) |
| |
| QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) |
| { |
| #if QT_CONFIG(filedialog) |
| const QStringList &filter = FilePickerController::nameFilters(acceptedMimeTypes); |
| QStringList ret; |
| QString str; |
| switch (static_cast<FilePickerController::FileChooserMode>(mode)) { |
| case FilePickerController::OpenMultiple: |
| ret = QFileDialog::getOpenFileNames(view(), QString(), QString(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails); |
| break; |
| // Chromium extension, not exposed as part of the public API for now. |
| case FilePickerController::UploadFolder: |
| str = QFileDialog::getExistingDirectory(view(), tr("Select folder to upload")); |
| if (!str.isNull()) |
| ret << str; |
| break; |
| case FilePickerController::Save: |
| str = QFileDialog::getSaveFileName(view(), QString(), (QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + oldFiles.first())); |
| if (!str.isNull()) |
| ret << str; |
| break; |
| case FilePickerController::Open: |
| str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails); |
| if (!str.isNull()) |
| ret << str; |
| break; |
| } |
| return ret; |
| #else |
| Q_UNUSED(mode); |
| Q_UNUSED(oldFiles); |
| Q_UNUSED(acceptedMimeTypes); |
| |
| return QStringList(); |
| #endif // QT_CONFIG(filedialog) |
| } |
| |
| void QWebEnginePage::javaScriptAlert(const QUrl &securityOrigin, const QString &msg) |
| { |
| Q_UNUSED(securityOrigin); |
| #if QT_CONFIG(messagebox) |
| QMessageBox::information(view(), QStringLiteral("Javascript Alert - %1").arg(url().toString()), msg); |
| #else |
| Q_UNUSED(msg); |
| #endif // QT_CONFIG(messagebox) |
| } |
| |
| bool QWebEnginePage::javaScriptConfirm(const QUrl &securityOrigin, const QString &msg) |
| { |
| Q_UNUSED(securityOrigin); |
| #if QT_CONFIG(messagebox) |
| return (QMessageBox::information(view(), QStringLiteral("Javascript Confirm - %1").arg(url().toString()), msg, QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok); |
| #else |
| Q_UNUSED(msg); |
| return false; |
| #endif // QT_CONFIG(messagebox) |
| } |
| |
| bool QWebEnginePage::javaScriptPrompt(const QUrl &securityOrigin, const QString &msg, const QString &defaultValue, QString *result) |
| { |
| Q_UNUSED(securityOrigin); |
| #if QT_CONFIG(inputdialog) |
| bool ret = false; |
| if (result) |
| *result = QInputDialog::getText(view(), QStringLiteral("Javascript Prompt - %1").arg(url().toString()), msg, QLineEdit::Normal, defaultValue, &ret); |
| return ret; |
| #else |
| Q_UNUSED(msg); |
| Q_UNUSED(defaultValue); |
| Q_UNUSED(result); |
| return false; |
| #endif // QT_CONFIG(inputdialog) |
| } |
| |
| void QWebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) |
| { |
| static QLoggingCategory loggingCategory("js", QtWarningMsg); |
| static QByteArray file = sourceID.toUtf8(); |
| QMessageLogger logger(file.constData(), lineNumber, nullptr, loggingCategory.categoryName()); |
| |
| switch (level) { |
| case JavaScriptConsoleMessageLevel::InfoMessageLevel: |
| if (loggingCategory.isInfoEnabled()) |
| logger.info().noquote() << message; |
| break; |
| case JavaScriptConsoleMessageLevel::WarningMessageLevel: |
| if (loggingCategory.isWarningEnabled()) |
| logger.warning().noquote() << message; |
| break; |
| case JavaScriptConsoleMessageLevel::ErrorMessageLevel: |
| if (loggingCategory.isCriticalEnabled()) |
| logger.critical().noquote() << message; |
| break; |
| } |
| } |
| |
| bool QWebEnginePage::certificateError(const QWebEngineCertificateError &) |
| { |
| return false; |
| } |
| |
| bool QWebEnginePage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) |
| { |
| Q_UNUSED(url); |
| Q_UNUSED(type); |
| Q_UNUSED(isMainFrame); |
| return true; |
| } |
| |
| QPointF QWebEnginePage::scrollPosition() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->lastScrollOffset(); |
| } |
| |
| QSizeF QWebEnginePage::contentsSize() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->lastContentsSize(); |
| } |
| |
| /*! |
| 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 pageLayout. |
| |
| 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(). |
| |
| If a file already exists at the provided file path, it will be overwritten. |
| \since 5.7 |
| \sa pdfPrintingFinished() |
| */ |
| void QWebEnginePage::printToPdf(const QString &filePath, const QPageLayout &pageLayout) |
| { |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| Q_D(const QWebEnginePage); |
| if (d->currentPrinter) { |
| qWarning("Cannot print to PDF while at the same time printing on printer %ls", qUtf16Printable(d->currentPrinter->printerName())); |
| return; |
| } |
| d->ensureInitialized(); |
| d->adapter->printToPDF(pageLayout, filePath); |
| #else |
| Q_UNUSED(filePath); |
| Q_UNUSED(pageLayout); |
| #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 pageLayout. |
| |
| 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. |
| |
| \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. |
| |
| \since 5.7 |
| */ |
| void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &resultCallback, const QPageLayout &pageLayout) |
| { |
| Q_D(QWebEnginePage); |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| if (d->currentPrinter) { |
| qWarning("Cannot print to PDF while at the same time printing on printer %ls", qUtf16Printable(d->currentPrinter->printerName())); |
| d->m_callbacks.invokeEmpty(resultCallback); |
| return; |
| } |
| d->ensureInitialized(); |
| quint64 requestId = d->adapter->printToPDFCallbackResult(pageLayout); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| #else |
| Q_UNUSED(pageLayout); |
| d->m_callbacks.invokeEmpty(resultCallback); |
| #endif |
| } |
| |
| /*! |
| Renders the current content of the page into a temporary PDF document, then prints it using \a printer. |
| |
| The settings for creating and printing the PDF document will be retrieved from the \a printer |
| object. |
| It is the users responsibility to ensure the \a printer remains valid until \a resultCallback |
| has been called. |
| |
| \note Printing runs on the browser process, which is by default not sandboxed. |
| |
| The \a resultCallback must take a boolean as parameter. If printing was successful, this |
| boolean will have the value \c true, otherwise, its value will be \c false. |
| |
| \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. |
| |
| \since 5.8 |
| */ |
| void QWebEnginePage::print(QPrinter *printer, const QWebEngineCallback<bool> &resultCallback) |
| { |
| Q_D(QWebEnginePage); |
| #if QT_CONFIG(webengine_printing_and_pdf) |
| if (d->currentPrinter) { |
| qWarning("Cannot print page on printer %ls: Already printing on %ls.", qUtf16Printable(printer->printerName()), qUtf16Printable(d->currentPrinter->printerName())); |
| d->m_callbacks.invokeDirectly(resultCallback, false); |
| return; |
| } |
| d->currentPrinter = printer; |
| d->ensureInitialized(); |
| quint64 requestId = d->adapter->printToPDFCallbackResult(printer->pageLayout(), |
| printer->colorMode() == QPrinter::Color, |
| false); |
| d->m_callbacks.registerCallback(requestId, resultCallback); |
| #else |
| Q_UNUSED(printer); |
| d->m_callbacks.invokeDirectly(resultCallback, false); |
| #endif |
| } |
| |
| /*! |
| \since 5.7 |
| |
| Returns additional data about the current context menu. It is only guaranteed to be valid during the call to the QWebEngineView::contextMenuEvent() |
| handler of the associated QWebEngineView. |
| |
| \sa createStandardContextMenu() |
| */ |
| const QWebEngineContextMenuData &QWebEnginePage::contextMenuData() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->contextData; |
| } |
| |
| /*! |
| \enum QWebEnginePage::LifecycleState |
| \since 5.14 |
| |
| This enum describes the lifecycle state of the page: |
| |
| \value Active |
| Normal state. |
| \value Frozen |
| Low CPU usage state where most HTML task sources are suspended. |
| \value Discarded |
| Very low resource usage state where the entire browsing context is discarded. |
| |
| \sa lifecycleState, {Page Lifecycle API}, {WebEngine Lifecycle Example} |
| */ |
| |
| /*! |
| \property QWebEnginePage::lifecycleState |
| \since 5.14 |
| |
| \brief The current lifecycle state of the page. |
| |
| The following restrictions are enforced by the setter: |
| |
| \list |
| \li A \l{visible} page must remain in the \c{Active} state. |
| \li If the page is being inspected by a \l{devToolsPage} then both pages must |
| remain in the \c{Active} states. |
| \li A page in the \c{Discarded} state can only transition to the \c{Active} |
| state. This will cause a reload of the page. |
| \endlist |
| |
| These are the only hard limits on the lifecycle state, but see also |
| \l{recommendedState} for the recommended soft limits. |
| |
| \sa recommendedState, {Page Lifecycle API}, {WebEngine Lifecycle Example} |
| */ |
| |
| QWebEnginePage::LifecycleState QWebEnginePage::lifecycleState() const |
| { |
| Q_D(const QWebEnginePage); |
| return static_cast<LifecycleState>(d->adapter->lifecycleState()); |
| } |
| |
| void QWebEnginePage::setLifecycleState(LifecycleState state) |
| { |
| Q_D(QWebEnginePage); |
| d->adapter->setLifecycleState(static_cast<WebContentsAdapterClient::LifecycleState>(state)); |
| } |
| |
| /*! |
| \property QWebEnginePage::recommendedState |
| \since 5.14 |
| |
| \brief The recommended limit for the lifecycle state of the page. |
| |
| Setting the lifecycle state to a lower resource usage state than the |
| recommended state may cause side-effects such as stopping background audio |
| playback or loss of HTML form input. Setting the lifecycle state to a higher |
| resource state is however completely safe. |
| |
| \sa lifecycleState, {Page Lifecycle API}, {WebEngine Lifecycle Example} |
| */ |
| |
| QWebEnginePage::LifecycleState QWebEnginePage::recommendedState() const |
| { |
| Q_D(const QWebEnginePage); |
| return static_cast<LifecycleState>(d->adapter->recommendedState()); |
| } |
| |
| /*! |
| \property QWebEnginePage::visible |
| \since 5.14 |
| |
| \brief Whether the page is considered visible in the Page Visibility API. |
| |
| Setting this property changes the \c{Document.hidden} and the |
| \c{Document.visibilityState} properties in JavaScript which web sites can use |
| to voluntarily reduce their resource usage if they are not visible to the |
| user. |
| |
| If the page is connected to a \l{view} then this property will be managed |
| automatically by the view according to it's own visibility. |
| |
| \sa lifecycleState |
| */ |
| |
| bool QWebEnginePage::isVisible() const |
| { |
| Q_D(const QWebEnginePage); |
| return d->adapter->isVisible(); |
| } |
| |
| void QWebEnginePage::setVisible(bool visible) |
| { |
| Q_D(QWebEnginePage); |
| |
| if (!d->adapter->isInitialized()) { |
| // On the one hand, it is too early to initialize here. The application |
| // may call show() before load(), or it may call show() from |
| // createWindow(), and then we would create an unnecessary blank |
| // WebContents here. On the other hand, if the application calls show() |
| // then it expects something to be shown, so we have to initialize. |
| // Therefore we have to delay the initialization via the event loop. |
| if (visible) |
| d->wasShownTimer.start(); |
| else |
| d->wasShownTimer.stop(); |
| return; |
| } |
| |
| d->adapter->setVisible(visible); |
| } |
| |
| #if QT_CONFIG(action) |
| QContextMenuBuilder::QContextMenuBuilder(const QtWebEngineCore::WebEngineContextMenuData &data, |
| QWebEnginePage *page, |
| QMenu *menu) |
| : QtWebEngineCore::RenderViewContextMenuQt(data) |
| , m_page(page) |
| , m_menu(menu) |
| { |
| } |
| |
| bool QContextMenuBuilder::hasInspector() |
| { |
| return m_page->d_ptr->adapter->hasInspector(); |
| } |
| |
| bool QContextMenuBuilder::isFullScreenMode() |
| { |
| return m_page->d_ptr->isFullScreenMode(); |
| } |
| |
| void QContextMenuBuilder::addMenuItem(ContextMenuItem menuItem) |
| { |
| QPointer<QWebEnginePage> thisRef(m_page); |
| QAction *action = 0; |
| |
| switch (menuItem) { |
| case ContextMenuItem::Back: |
| action = thisRef->action(QWebEnginePage::Back); |
| break; |
| case ContextMenuItem::Forward: |
| action = thisRef->action(QWebEnginePage::Forward); |
| break; |
| case ContextMenuItem::Reload: |
| action = thisRef->action(QWebEnginePage::Reload); |
| break; |
| case ContextMenuItem::Cut: |
| action = thisRef->action(QWebEnginePage::Cut); |
| break; |
| case ContextMenuItem::Copy: |
| action = thisRef->action(QWebEnginePage::Copy); |
| break; |
| case ContextMenuItem::Paste: |
| action = thisRef->action(QWebEnginePage::Paste); |
| break; |
| case ContextMenuItem::Undo: |
| action = thisRef->action(QWebEnginePage::Undo); |
| break; |
| case ContextMenuItem::Redo: |
| action = thisRef->action(QWebEnginePage::Redo); |
| break; |
| case ContextMenuItem::SelectAll: |
| action = thisRef->action(QWebEnginePage::SelectAll); |
| break; |
| case ContextMenuItem::PasteAndMatchStyle: |
| action = thisRef->action(QWebEnginePage::PasteAndMatchStyle); |
| break; |
| case ContextMenuItem::OpenLinkInNewWindow: |
| action = thisRef->action(QWebEnginePage::OpenLinkInNewWindow); |
| break; |
| case ContextMenuItem::OpenLinkInNewTab: |
| action = thisRef->action(QWebEnginePage::OpenLinkInNewTab); |
| break; |
| case ContextMenuItem::CopyLinkToClipboard: |
| action = thisRef->action(QWebEnginePage::CopyLinkToClipboard); |
| break; |
| case ContextMenuItem::DownloadLinkToDisk: |
| action = thisRef->action(QWebEnginePage::DownloadLinkToDisk); |
| break; |
| case ContextMenuItem::CopyImageToClipboard: |
| action = thisRef->action(QWebEnginePage::CopyImageToClipboard); |
| break; |
| case ContextMenuItem::CopyImageUrlToClipboard: |
| action = thisRef->action(QWebEnginePage::CopyImageUrlToClipboard); |
| break; |
| case ContextMenuItem::DownloadImageToDisk: |
| action = thisRef->action(QWebEnginePage::DownloadImageToDisk); |
| break; |
| case ContextMenuItem::CopyMediaUrlToClipboard: |
| action = thisRef->action(QWebEnginePage::CopyMediaUrlToClipboard); |
| break; |
| case ContextMenuItem::ToggleMediaControls: |
| action = thisRef->action(QWebEnginePage::ToggleMediaControls); |
| break; |
| case ContextMenuItem::ToggleMediaLoop: |
| action = thisRef->action(QWebEnginePage::ToggleMediaLoop); |
| break; |
| case ContextMenuItem::DownloadMediaToDisk: |
| action = thisRef->action(QWebEnginePage::DownloadMediaToDisk); |
| break; |
| case ContextMenuItem::InspectElement: |
| action = thisRef->action(QWebEnginePage::InspectElement); |
| break; |
| case ContextMenuItem::ExitFullScreen: |
| action = thisRef->action(QWebEnginePage::ExitFullScreen); |
| break; |
| case ContextMenuItem::SavePage: |
| action = thisRef->action(QWebEnginePage::SavePage); |
| break; |
| case ContextMenuItem::ViewSource: |
| action = thisRef->action(QWebEnginePage::ViewSource); |
| break; |
| case ContextMenuItem::SpellingSuggestions: |
| for (int i=0; i < m_contextData.spellCheckerSuggestions().count() && i < 4; i++) { |
| action = new QAction(m_menu); |
| QString replacement = m_contextData.spellCheckerSuggestions().at(i); |
| QObject::connect(action, &QAction::triggered, [thisRef, replacement] { if (thisRef) thisRef->replaceMisspelledWord(replacement); }); |
| action->setText(replacement); |
| m_menu->addAction(action); |
| } |
| return; |
| case ContextMenuItem::Separator: |
| if (!m_menu->isEmpty()) |
| m_menu->addSeparator(); |
| return; |
| } |
| action->setEnabled(isMenuItemEnabled(menuItem)); |
| m_menu->addAction(action); |
| } |
| |
| bool QContextMenuBuilder::isMenuItemEnabled(ContextMenuItem menuItem) |
| { |
| switch (menuItem) { |
| case ContextMenuItem::Back: |
| return m_page->d_ptr->adapter->canGoBack(); |
| case ContextMenuItem::Forward: |
| return m_page->d_ptr->adapter->canGoForward(); |
| case ContextMenuItem::Reload: |
| return true; |
| case ContextMenuItem::Cut: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanCut; |
| case ContextMenuItem::Copy: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanCopy; |
| case ContextMenuItem::Paste: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanPaste; |
| case ContextMenuItem::Undo: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanUndo; |
| case ContextMenuItem::Redo: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanRedo; |
| case ContextMenuItem::SelectAll: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanSelectAll; |
| case ContextMenuItem::PasteAndMatchStyle: |
| return m_contextData.editFlags() & QtWebEngineCore::WebEngineContextMenuData::CanPaste; |
| case ContextMenuItem::OpenLinkInNewWindow: |
| case ContextMenuItem::OpenLinkInNewTab: |
| case ContextMenuItem::CopyLinkToClipboard: |
| case ContextMenuItem::DownloadLinkToDisk: |
| case ContextMenuItem::CopyImageToClipboard: |
| case ContextMenuItem::CopyImageUrlToClipboard: |
| case ContextMenuItem::DownloadImageToDisk: |
| case ContextMenuItem::CopyMediaUrlToClipboard: |
| case ContextMenuItem::ToggleMediaControls: |
| case ContextMenuItem::ToggleMediaLoop: |
| case ContextMenuItem::DownloadMediaToDisk: |
| case ContextMenuItem::InspectElement: |
| case ContextMenuItem::ExitFullScreen: |
| case ContextMenuItem::SavePage: |
| return true; |
| case ContextMenuItem::ViewSource: |
| return m_page->d_ptr->adapter->canViewSource(); |
| case ContextMenuItem::SpellingSuggestions: |
| case ContextMenuItem::Separator: |
| return true; |
| } |
| Q_UNREACHABLE(); |
| } |
| #endif // QT_CONFIG(action) |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qwebenginepage.cpp" |