diff options
Diffstat (limited to 'src/webenginewidgets/api/qwebenginepage.cpp')
-rw-r--r-- | src/webenginewidgets/api/qwebenginepage.cpp | 582 |
1 files changed, 401 insertions, 181 deletions
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 61feec244..f8f96af6a 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -23,9 +23,12 @@ #include "qwebenginepage.h" #include "qwebenginepage_p.h" +#include "authentication_dialog_controller.h" #include "browser_context_adapter.h" #include "certificate_error_controller.h" +#include "file_picker_controller.h" #include "javascript_dialog_controller.h" +#include "qwebenginefullscreenrequest.h" #include "qwebenginehistory.h" #include "qwebenginehistory_p.h" #include "qwebengineprofile.h" @@ -52,8 +55,10 @@ #include <QIcon> #include <QInputDialog> #include <QLayout> +#include <QLoggingCategory> #include <QMenu> #include <QMessageBox> +#include <QMimeData> #include <QStandardPaths> #include <QStyle> #include <QUrl> @@ -77,106 +82,6 @@ static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::Wind } } -CallbackDirectory::~CallbackDirectory() -{ - // "Cancel" pending callbacks by calling them with an invalid value. - // This guarantees that each callback is called exactly once. - Q_FOREACH (const CallbackSharedDataPointer &sharedPtr, m_callbackMap) { - switch (sharedPtr.type) { - case CallbackSharedDataPointer::Variant: - (*sharedPtr.variantCallback)(QVariant()); - break; - case CallbackSharedDataPointer::String: - (*sharedPtr.stringCallback)(QString()); - break; - case CallbackSharedDataPointer::Bool: - (*sharedPtr.boolCallback)(false); - break; - default: - Q_UNREACHABLE(); - } - } -} - -void CallbackDirectory::registerCallback(quint64 requestId, const QExplicitlySharedDataPointer<VariantCallback> &callback) -{ - m_callbackMap.insert(requestId, CallbackSharedDataPointer(callback.data())); -} - -void CallbackDirectory::registerCallback(quint64 requestId, const QExplicitlySharedDataPointer<StringCallback> &callback) -{ - m_callbackMap.insert(requestId, CallbackSharedDataPointer(callback.data())); -} - -void CallbackDirectory::registerCallback(quint64 requestId, const QExplicitlySharedDataPointer<BoolCallback> &callback) -{ - m_callbackMap.insert(requestId, CallbackSharedDataPointer(callback.data())); -} - -void CallbackDirectory::invoke(quint64 requestId, const QVariant &result) -{ - CallbackSharedDataPointer sharedPtr = m_callbackMap.take(requestId); - if (sharedPtr) { - Q_ASSERT(sharedPtr.type == CallbackSharedDataPointer::Variant); - (*sharedPtr.variantCallback)(result); - } -} - -void CallbackDirectory::invoke(quint64 requestId, const QString &result) -{ - CallbackSharedDataPointer sharedPtr = m_callbackMap.take(requestId); - if (sharedPtr) { - Q_ASSERT(sharedPtr.type == CallbackSharedDataPointer::String); - (*sharedPtr.stringCallback)(result); - } -} - -void CallbackDirectory::invoke(quint64 requestId, bool result) -{ - CallbackSharedDataPointer sharedPtr = m_callbackMap.take(requestId); - if (sharedPtr) { - Q_ASSERT(sharedPtr.type == CallbackSharedDataPointer::Bool); - (*sharedPtr.boolCallback)(result); - } -} - -void CallbackDirectory::CallbackSharedDataPointer::doRef() -{ - switch (type) { - case None: - break; - case Variant: - variantCallback->ref.ref(); - break; - case String: - stringCallback->ref.ref(); - break; - case Bool: - boolCallback->ref.ref(); - break; - } -} - -void CallbackDirectory::CallbackSharedDataPointer::doDeref() -{ - switch (type) { - case None: - break; - case Variant: - if (!variantCallback->ref.deref()) - delete variantCallback; - break; - case String: - if (!stringCallback->ref.deref()) - delete stringCallback; - break; - case Bool: - if (!boolCallback->ref.deref()) - delete boolCallback; - break; - } -} - QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) : adapter(new WebContentsAdapter) , history(new QWebEngineHistory(new QWebEngineHistoryPrivate(this))) @@ -186,6 +91,8 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) , isLoading(false) , scriptCollection(new QWebEngineScriptCollectionPrivate(browserContextAdapter()->userScriptController(), adapter.data())) , m_isBeingAdopted(false) + , m_backgroundColor(Qt::white) + , fullscreenMode(false) { memset(actions, 0, sizeof(actions)); } @@ -248,6 +155,11 @@ qreal QWebEnginePagePrivate::dpiScale() const return 1.0; } +QColor QWebEnginePagePrivate::backgroundColor() const +{ + return m_backgroundColor; +} + void QWebEnginePagePrivate::loadStarted(const QUrl &provisionalUrl, bool isErrorPage) { Q_UNUSED(provisionalUrl); @@ -336,6 +248,11 @@ void QWebEnginePagePrivate::close() Q_EMIT q->windowCloseRequested(); } +void QWebEnginePagePrivate::windowCloseRejected() +{ + // Do nothing for now. +} + void QWebEnginePagePrivate::didRunJavaScript(quint64 requestId, const QVariant& result) { m_callbacks.invoke(requestId, result); @@ -362,18 +279,24 @@ void QWebEnginePagePrivate::passOnFocus(bool reverse) view->focusNextPrevChild(!reverse); } -void QWebEnginePagePrivate::authenticationRequired(const QUrl &requestUrl, const QString &realm, bool isProxy, const QString &challengingHost, QString *outUser, QString *outPassword) +void QWebEnginePagePrivate::authenticationRequired(QSharedPointer<AuthenticationDialogController> controller) { Q_Q(QWebEnginePage); QAuthenticator networkAuth; - networkAuth.setRealm(realm); + networkAuth.setRealm(controller->realm()); - if (isProxy) - Q_EMIT q->proxyAuthenticationRequired(requestUrl, &networkAuth, challengingHost); + if (controller->isProxy()) + Q_EMIT q->proxyAuthenticationRequired(controller->url(), &networkAuth, controller->host()); else - Q_EMIT q->authenticationRequired(requestUrl, &networkAuth); - *outUser = networkAuth.user(); - *outPassword = networkAuth.password(); + 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::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags) @@ -473,6 +396,14 @@ void QWebEnginePagePrivate::recreateFromSerializedHistory(QDataStream &input) } } +void QWebEnginePagePrivate::setFullScreenMode(bool fullscreen) +{ + if (fullscreenMode != fullscreen) { + fullscreenMode = fullscreen; + adapter->changedFullScreen(); + } +} + BrowserContextAdapter *QWebEnginePagePrivate::browserContextAdapter() { return profile->d_ptr->browserContext(); @@ -488,10 +419,49 @@ QWebEnginePage::QWebEnginePage(QObject* parent) } /*! - Constructs an empty QWebEnginePage in the QWebEngineProfile \a profile with parent \a parent. + \enum QWebEnginePage::RenderProcessTerminationStatus + + 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) + + 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 request) + + This signal is emitted when the web page issues the request to enter fullscreen mode for + a web-element, usually a video element. - If the profile is not the default profile the caller must ensure the profile is alive for as - long as the page is. + The request object \a request 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 +*/ + +/*! + 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 */ @@ -523,12 +493,12 @@ QWebEngineSettings *QWebEnginePage::settings() const } /*! - * Returns a pointer to the web channel instance used by this page, or a null pointer if none was set. - * This channel is automatically using the internal QtWebEngine transport mechanism over Chromium IPC, - * and exposed in the javascript context of this page as \c qt.webChannelTransport + * 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 {QtWebChannel::QWebChannel}{QWebChannel} + * \sa QWebChannel */ QWebChannel *QWebEnginePage::webChannel() const { @@ -537,14 +507,14 @@ QWebChannel *QWebEnginePage::webChannel() const } /*! - * Sets the web channel instance to be used by this page and connects it to QtWebEngine's transport - * using Chromium IPC messages. That transport is exposed in the javascript context of this page as + * 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 + * context of this page 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 \a channel object. + * \note The page does not take ownership of the channel object. * * \since 5.5 - * \param channel */ void QWebEnginePage::setWebChannel(QWebChannel *channel) @@ -553,6 +523,33 @@ void QWebEnginePage::setWebChannel(QWebChannel *channel) d->adapter->setWebChannel(channel); } +/*! + \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->backgroundColorChanged(); +} + void QWebEnginePage::setView(QWidget *view) { QWebEngineViewPrivate::bind(qobject_cast<QWebEngineView*>(view), this); @@ -565,7 +562,7 @@ QWidget *QWebEnginePage::view() const } /*! - Returns the QWebEngineProfile the page belongs to. + Returns the web engine profile the page belongs to. \since 5.5 */ QWebEngineProfile *QWebEnginePage::profile() const @@ -636,6 +633,57 @@ QAction *QWebEnginePage::action(WebAction action) const case PasteAndMatchStyle: text = tr("Paste and Match Style"); break; + case OpenLinkInThisWindow: + text = tr("Open Link in This Window"); + break; + case OpenLinkInNewWindow: + text = tr("Open Link in New Window"); + break; + case OpenLinkInNewTab: + text = tr("Open Link in New Tab"); + break; + case CopyLinkToClipboard: + text = tr("Copy Link URL"); + break; + case DownloadLinkToDisk: + text = tr("Save Link"); + break; + case CopyImageToClipboard: + text = tr("Copy Image"); + break; + case CopyImageUrlToClipboard: + text = tr("Copy Image URL"); + break; + case DownloadImageToDisk: + text = tr("Save Image"); + break; + case CopyMediaUrlToClipboard: + text = tr("Copy Media URL"); + break; + case ToggleMediaControls: + text = tr("Toggle Media Controls"); + break; + case ToggleMediaLoop: + text = tr("Toggle Looping"); + break; + case ToggleMediaPlayPause: + text = tr("Toggle Play/Pause"); + break; + case ToggleMediaMute: + text = tr("Toggle Mute"); + break; + case DownloadMediaToDisk: + text = tr("Save Media"); + break; + case InspectElement: + text = tr("Inspect Element"); + break; + case ExitFullScreen: + text = tr("Exit Full Screen Mode"); + break; + case RequestClose: + text = tr("Close Page"); + break; default: break; } @@ -693,6 +741,122 @@ void QWebEnginePage::triggerAction(WebAction action, bool) case PasteAndMatchStyle: d->adapter->pasteAndMatchStyle(); break; + case OpenLinkInThisWindow: + if (d->m_menuData.linkUrl.isValid()) + setUrl(d->m_menuData.linkUrl); + break; + case OpenLinkInNewWindow: + if (d->m_menuData.linkUrl.isValid()) { + QWebEnginePage *newPage = createWindow(WebBrowserWindow); + if (newPage) + newPage->setUrl(d->m_menuData.linkUrl); + } + break; + case OpenLinkInNewTab: + if (d->m_menuData.linkUrl.isValid()) { + QWebEnginePage *newPage = createWindow(WebBrowserTab); + if (newPage) + newPage->setUrl(d->m_menuData.linkUrl); + } + break; + case CopyLinkToClipboard: + if (d->m_menuData.linkUrl.isValid()) { + QString urlString = d->m_menuData.linkUrl.toString(QUrl::FullyEncoded); + QString title = d->m_menuData.linkText.toHtmlEscaped(); + QMimeData *data = new QMimeData(); + data->setText(urlString); + QString html = QStringLiteral("<a href=\"") + urlString + QStringLiteral("\">") + title + QStringLiteral("</a>"); + data->setHtml(html); + data->setUrls(QList<QUrl>() << d->m_menuData.linkUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case DownloadLinkToDisk: + if (d->m_menuData.linkUrl.isValid()) + d->adapter->download(d->m_menuData.linkUrl, d->m_menuData.suggestedFileName); + break; + case CopyImageToClipboard: + if (d->m_menuData.hasImageContent && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeImage || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeCanvas)) + { + d->adapter->copyImageAt(d->m_menuData.pos); + } + break; + case CopyImageUrlToClipboard: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeImage) { + QString urlString = d->m_menuData.mediaUrl.toString(QUrl::FullyEncoded); + QString title = d->m_menuData.linkText; + if (!title.isEmpty()) + title = QStringLiteral(" alt=\"%1\"").arg(title.toHtmlEscaped()); + QMimeData *data = new QMimeData(); + data->setText(urlString); + QString html = QStringLiteral("<img src=\"") + urlString + QStringLiteral("\"") + title + QStringLiteral("></img>"); + data->setHtml(html); + data->setUrls(QList<QUrl>() << d->m_menuData.mediaUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case DownloadImageToDisk: + case DownloadMediaToDisk: + if (d->m_menuData.mediaUrl.isValid()) + d->adapter->download(d->m_menuData.mediaUrl, d->m_menuData.suggestedFileName); + break; + case CopyMediaUrlToClipboard: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + QString urlString = d->m_menuData.mediaUrl.toString(QUrl::FullyEncoded); + QMimeData *data = new QMimeData(); + data->setText(urlString); + if (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio) + data->setHtml(QStringLiteral("<audio src=\"") + urlString + QStringLiteral("\"></audio>")); + else + data->setHtml(QStringLiteral("<video src=\"") + urlString + QStringLiteral("\"></video>")); + data->setUrls(QList<QUrl>() << d->m_menuData.mediaUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case ToggleMediaControls: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaCanToggleControls) { + bool enable = !(d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaControls); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerControls, enable); + } + break; + case ToggleMediaLoop: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + bool enable = !(d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaLoop); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerLoop, enable); + } + break; + case ToggleMediaPlayPause: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + bool enable = (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaPaused); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerPlay, enable); + } + break; + case ToggleMediaMute: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaHasAudio) { + bool enable = (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaMuted); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerMute, enable); + } + break; + case InspectElement: + d->adapter->inspectElementAt(d->m_menuData.pos); + break; + case ExitFullScreen: + d->adapter->exitFullScreen(); + break; + case RequestClose: + d->adapter->requestClose(); + break; default: Q_UNREACHABLE(); } @@ -703,12 +867,10 @@ void QWebEnginePage::findText(const QString &subString, FindFlags options, const Q_D(QWebEnginePage); if (subString.isEmpty()) { d->adapter->stopFinding(); - if (resultCallback.d) - (*resultCallback.d)(false); + d->m_callbacks.invokeEmpty(resultCallback); } else { quint64 requestId = d->adapter->findText(subString, options & FindCaseSensitively, options & FindBackward); - if (resultCallback.d) - d->m_callbacks.registerCallback(requestId, resultCallback.d); + d->m_callbacks.registerCallback(requestId, resultCallback); } } @@ -720,11 +882,22 @@ bool QWebEnginePage::event(QEvent *e) return QObject::event(e); } +void QWebEnginePagePrivate::wasShown() +{ + adapter->wasShown(); +} + +void QWebEnginePagePrivate::wasHidden() +{ + adapter->wasHidden(); +} + bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData &data) { if (!view || !view->d_func()->m_pendingContextMenuEvent) return false; + m_menuData = WebEngineContextMenuData(); QContextMenuEvent event(QContextMenuEvent::Mouse, data.pos, view->mapToGlobal(data.pos)); switch (view->contextMenuPolicy()) { case Qt::PreventContextMenu: @@ -748,7 +921,6 @@ bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData break; } view->d_func()->m_pendingContextMenuEvent = false; - m_menuData = WebEngineContextMenuData(); return true; } @@ -759,6 +931,18 @@ void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl & 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); @@ -777,6 +961,9 @@ void QWebEnginePagePrivate::javascriptDialog(QSharedPointer<JavaScriptDialogCont if (accepted) controller->textProvided(promptResult); break; + case UnloadDialog: + accepted = (QMessageBox::information(view, QCoreApplication::translate("QWebEnginePage", "Are you sure you want to leave this page?"), controller->message(), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes); + break; case InternalAuthorizationDialog: accepted = (QMessageBox::question(view, controller->title(), controller->message(), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes); break; @@ -828,34 +1015,12 @@ void QWebEnginePagePrivate::moveValidationMessage(const QRect &anchor) #endif } -namespace { -class SaveToClipboardFunctor +void QWebEnginePagePrivate::renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, + int exitCode) { - QString m_text; -public: - SaveToClipboardFunctor(const QString &text) - : m_text(text) - {} - void operator()() const - { - qApp->clipboard()->setText(m_text); - } -}; - -class LoadUrlFunctor -{ - QWebEnginePage *m_page; - QUrl m_url; -public: - LoadUrlFunctor(QWebEnginePage *page, const QUrl &url) - : m_page(page) - , m_url(url) - {} - void operator()() const - { - m_page->load(m_url); - } -}; + Q_Q(QWebEnginePage); + Q_EMIT q->renderProcessTerminated(static_cast<QWebEnginePage::RenderProcessTerminationStatus>( + terminationStatus), exitCode); } QMenu *QWebEnginePage::createStandardContextMenu() @@ -864,6 +1029,12 @@ QMenu *QWebEnginePage::createStandardContextMenu() QMenu *menu = new QMenu(d->view); QAction *action = 0; WebEngineContextMenuData contextMenuData(d->m_menuData); + if (!contextMenuData.linkText.isEmpty() && contextMenuData.linkUrl.isValid()) { + action = QWebEnginePage::action(OpenLinkInThisWindow); + action->setText(tr("Follow Link")); + menu->addAction(action); + menu->addAction(QWebEnginePage::action(DownloadLinkToDisk)); + } if (contextMenuData.selectedText.isEmpty()) { action = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), tr("&Back"), menu); connect(action, &QAction::triggered, d->view, &QWebEngineView::back); @@ -879,20 +1050,46 @@ QMenu *QWebEnginePage::createStandardContextMenu() connect(action, &QAction::triggered, d->view, &QWebEngineView::reload); menu->addAction(action); } else { - action = new QAction(tr("Copy..."), menu); - connect(action, &QAction::triggered, SaveToClipboardFunctor(contextMenuData.selectedText)); - menu->addAction(action); + menu->addAction(QWebEnginePage::action(Copy)); } if (!contextMenuData.linkText.isEmpty() && contextMenuData.linkUrl.isValid()) { - menu->addSeparator(); - action = new QAction(tr("Navigate to..."), menu); - connect(action, &QAction::triggered, LoadUrlFunctor(this, contextMenuData.linkUrl)); - menu->addAction(action); - action = new QAction(tr("Copy link address"), menu); - connect(action, &QAction::triggered, SaveToClipboardFunctor(contextMenuData.linkUrl.toString())); - menu->addAction(action); + menu->addAction(QWebEnginePage::action(CopyLinkToClipboard)); } + if (contextMenuData.mediaUrl.isValid()) { + switch (contextMenuData.mediaType) { + case WebEngineContextMenuData::MediaTypeImage: + menu->addAction(QWebEnginePage::action(DownloadImageToDisk)); + menu->addAction(QWebEnginePage::action(CopyImageUrlToClipboard)); + menu->addAction(QWebEnginePage::action(CopyImageToClipboard)); + break; + case WebEngineContextMenuData::MediaTypeCanvas: + Q_UNREACHABLE(); // mediaUrl is invalid for canvases + break; + case WebEngineContextMenuData::MediaTypeAudio: + case WebEngineContextMenuData::MediaTypeVideo: + menu->addAction(QWebEnginePage::action(DownloadMediaToDisk)); + menu->addAction(QWebEnginePage::action(CopyMediaUrlToClipboard)); + menu->addAction(QWebEnginePage::action(ToggleMediaPlayPause)); + menu->addAction(QWebEnginePage::action(ToggleMediaLoop)); + if (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaHasAudio) + menu->addAction(QWebEnginePage::action(ToggleMediaMute)); + if (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaCanToggleControls) + menu->addAction(QWebEnginePage::action(ToggleMediaControls)); + break; + default: + break; + } + } else if (contextMenuData.mediaType == WebEngineContextMenuData::MediaTypeCanvas) { + menu->addAction(QWebEnginePage::action(CopyImageToClipboard)); + } + + if (d->adapter->hasInspector()) + menu->addAction(QWebEnginePage::action(InspectElement)); + + if (d->isFullScreenMode()) + menu->addAction(QWebEnginePage::action(ExitFullScreen)); + return menu; } @@ -935,17 +1132,22 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine } } -static inline QWebEnginePage::FileSelectionMode toPublic(WebContentsAdapterClient::FileChooserMode mode) +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(WebContentsAdapterClient::FileChooserMode mode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) +void QWebEnginePagePrivate::runFileChooser(FilePickerController *controller) { Q_Q(QWebEnginePage); - QStringList selectedFileNames = q->chooseFiles(toPublic(mode), (QStringList() << defaultFileName), acceptedMimeTypes); - adapter->filesSelectedInChooser(selectedFileNames, mode); + + 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 @@ -963,14 +1165,14 @@ void QWebEnginePage::toHtml(const QWebEngineCallback<const QString &> &resultCal { Q_D(const QWebEnginePage); quint64 requestId = d->adapter->fetchDocumentMarkup(); - d->m_callbacks.registerCallback(requestId, resultCallback.d); + d->m_callbacks.registerCallback(requestId, resultCallback); } void QWebEnginePage::toPlainText(const QWebEngineCallback<const QString &> &resultCallback) const { Q_D(const QWebEnginePage); quint64 requestId = d->adapter->fetchDocumentInnerText(); - d->m_callbacks.registerCallback(requestId, resultCallback.d); + d->m_callbacks.registerCallback(requestId, resultCallback); } void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl) @@ -1037,12 +1239,16 @@ void QWebEnginePage::runJavaScript(const QString& scriptSource, const QWebEngine { Q_D(QWebEnginePage); quint64 requestId = d->adapter->runJavaScriptCallbackResult(scriptSource); - d->m_callbacks.registerCallback(requestId, resultCallback.d); + d->m_callbacks.registerCallback(requestId, resultCallback); } /*! - Returns the script collection used by this page. - \sa QWebEngineScriptCollection + 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 */ QWebEngineScriptCollection &QWebEnginePage::scripts() @@ -1062,8 +1268,8 @@ QWebEnginePage *QWebEnginePage::createWindow(WebWindowType type) return 0; } -ASSERT_ENUMS_MATCH(WebContentsAdapterClient::Open, QWebEnginePage::FileSelectOpen) -ASSERT_ENUMS_MATCH(WebContentsAdapterClient::OpenMultiple, QWebEnginePage::FileSelectOpenMultiple) +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) { @@ -1072,23 +1278,23 @@ QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringLis Q_UNUSED(acceptedMimeTypes); QStringList ret; QString str; - switch (static_cast<WebContentsAdapterClient::FileChooserMode>(mode)) { - case WebContentsAdapterClient::OpenMultiple: + switch (static_cast<FilePickerController::FileChooserMode>(mode)) { + case FilePickerController::OpenMultiple: ret = QFileDialog::getOpenFileNames(view(), QString()); break; // Chromium extension, not exposed as part of the public API for now. - case WebContentsAdapterClient::UploadFolder: + case FilePickerController::UploadFolder: str = QFileDialog::getExistingDirectory(view(), tr("Select folder to upload")) + QLatin1Char('/'); if (!str.isNull()) ret << str; break; - case WebContentsAdapterClient::Save: + case FilePickerController::Save: str = QFileDialog::getSaveFileName(view(), QString(), (QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + oldFiles.first())); if (!str.isNull()) ret << str; break; default: - case WebContentsAdapterClient::Open: + case FilePickerController::Open: str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first()); if (!str.isNull()) ret << str; @@ -1120,10 +1326,24 @@ bool QWebEnginePage::javaScriptPrompt(const QUrl &securityOrigin, const QString void QWebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) { - Q_UNUSED(level); - Q_UNUSED(message); - Q_UNUSED(lineNumber); - Q_UNUSED(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 &) |