| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 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 "qwebengineview.h" |
| #include "qwebengineview_p.h" |
| |
| #include "qwebenginepage_p.h" |
| #include "render_widget_host_view_qt_delegate_widget.h" |
| #include "web_contents_adapter.h" |
| |
| #if QT_CONFIG(action) |
| #include <QAction> |
| #endif |
| #if QT_CONFIG(menu) |
| #include <QMenu> |
| #endif |
| #include <QContextMenuEvent> |
| #include <QToolTip> |
| #include <QVBoxLayout> |
| |
| QT_BEGIN_NAMESPACE |
| |
| void QWebEngineViewPrivate::pageChanged(QWebEnginePage *oldPage, QWebEnginePage *newPage) |
| { |
| Q_Q(QWebEngineView); |
| |
| if (oldPage) { |
| oldPage->setVisible(false); |
| oldPage->disconnect(q); |
| } |
| |
| if (newPage) { |
| QObject::connect(newPage, &QWebEnginePage::titleChanged, q, &QWebEngineView::titleChanged); |
| QObject::connect(newPage, &QWebEnginePage::urlChanged, q, &QWebEngineView::urlChanged); |
| QObject::connect(newPage, &QWebEnginePage::iconUrlChanged, q, &QWebEngineView::iconUrlChanged); |
| QObject::connect(newPage, &QWebEnginePage::iconChanged, q, &QWebEngineView::iconChanged); |
| QObject::connect(newPage, &QWebEnginePage::loadStarted, q, &QWebEngineView::loadStarted); |
| QObject::connect(newPage, &QWebEnginePage::loadProgress, q, &QWebEngineView::loadProgress); |
| QObject::connect(newPage, &QWebEnginePage::loadFinished, q, &QWebEngineView::loadFinished); |
| QObject::connect(newPage, &QWebEnginePage::selectionChanged, q, &QWebEngineView::selectionChanged); |
| QObject::connect(newPage, &QWebEnginePage::renderProcessTerminated, q, &QWebEngineView::renderProcessTerminated); |
| newPage->setVisible(q->isVisible()); |
| } |
| |
| auto oldUrl = oldPage ? oldPage->url() : QUrl(); |
| auto newUrl = newPage ? newPage->url() : QUrl(); |
| if (oldUrl != newUrl) |
| Q_EMIT q->urlChanged(newUrl); |
| |
| auto oldTitle = oldPage ? oldPage->title() : QString(); |
| auto newTitle = newPage ? newPage->title() : QString(); |
| if (oldTitle != newTitle) |
| Q_EMIT q->titleChanged(newTitle); |
| |
| auto oldIcon = oldPage ? oldPage->iconUrl() : QUrl(); |
| auto newIcon = newPage ? newPage->iconUrl() : QUrl(); |
| if (oldIcon != newIcon) { |
| Q_EMIT q->iconUrlChanged(newIcon); |
| Q_EMIT q->iconChanged(newPage ? newPage->icon() : QIcon()); |
| } |
| |
| if ((oldPage && oldPage->hasSelection()) || (newPage && newPage->hasSelection())) |
| Q_EMIT q->selectionChanged(); |
| } |
| |
| void QWebEngineViewPrivate::widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *oldWidget, |
| QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *newWidget) |
| { |
| Q_Q(QWebEngineView); |
| |
| if (oldWidget) { |
| q->layout()->removeWidget(oldWidget); |
| oldWidget->hide(); |
| } |
| |
| if (newWidget) { |
| q->layout()->addWidget(newWidget); |
| q->setFocusProxy(newWidget); |
| newWidget->show(); |
| } |
| } |
| |
| #ifndef QT_NO_ACCESSIBILITY |
| static QAccessibleInterface *webAccessibleFactory(const QString &, QObject *object) |
| { |
| if (QWebEngineView *v = qobject_cast<QWebEngineView*>(object)) |
| return new QWebEngineViewAccessible(v); |
| return nullptr; |
| } |
| #endif // QT_NO_ACCESSIBILITY |
| |
| QWebEngineViewPrivate::QWebEngineViewPrivate() |
| : page(0) |
| , m_dragEntered(false) |
| , m_ownsPage(false) |
| { |
| #ifndef QT_NO_ACCESSIBILITY |
| QAccessible::installFactory(&webAccessibleFactory); |
| #endif // QT_NO_ACCESSIBILITY |
| } |
| |
| /*! |
| \fn QWebEngineView::renderProcessTerminated(QWebEnginePage::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 void QWebEngineView::iconChanged(const QIcon &icon) |
| \since 5.7 |
| |
| This signal is emitted when the icon ("favicon") associated with the |
| view is changed. The new icon is specified by \a icon. |
| |
| \sa icon(), iconUrl(), iconUrlChanged() |
| */ |
| |
| QWebEngineView::QWebEngineView(QWidget *parent) |
| : QWidget(parent) |
| , d_ptr(new QWebEngineViewPrivate) |
| { |
| Q_D(QWebEngineView); |
| d->q_ptr = this; |
| setAcceptDrops(true); |
| |
| QVBoxLayout *layout = new QVBoxLayout; |
| layout->setContentsMargins(0, 0, 0, 0); |
| setLayout(layout); |
| } |
| |
| QWebEngineView::~QWebEngineView() |
| { |
| blockSignals(true); |
| QWebEnginePagePrivate::bindPageAndView(nullptr, this); |
| } |
| |
| QWebEnginePage* QWebEngineView::page() const |
| { |
| Q_D(const QWebEngineView); |
| if (!d->page) { |
| QWebEngineView *that = const_cast<QWebEngineView*>(this); |
| that->setPage(new QWebEnginePage(that)); |
| d->m_ownsPage = true; |
| } |
| return d->page; |
| } |
| |
| void QWebEngineView::setPage(QWebEnginePage *newPage) |
| { |
| QWebEnginePagePrivate::bindPageAndView(newPage, this); |
| } |
| |
| void QWebEngineView::load(const QUrl& url) |
| { |
| page()->load(url); |
| } |
| |
| /*! |
| \since 5.9 |
| Issues the specified \a request and loads the response. |
| |
| \sa load(), setUrl(), url(), urlChanged(), QUrl::fromUserInput() |
| */ |
| void QWebEngineView::load(const QWebEngineHttpRequest &request) |
| { |
| page()->load(request); |
| } |
| |
| void QWebEngineView::setHtml(const QString& html, const QUrl& baseUrl) |
| { |
| page()->setHtml(html, baseUrl); |
| } |
| |
| void QWebEngineView::setContent(const QByteArray& data, const QString& mimeType, const QUrl& baseUrl) |
| { |
| page()->setContent(data, mimeType, baseUrl); |
| } |
| |
| QWebEngineHistory* QWebEngineView::history() const |
| { |
| return page()->history(); |
| } |
| |
| QString QWebEngineView::title() const |
| { |
| return page()->title(); |
| } |
| |
| void QWebEngineView::setUrl(const QUrl &url) |
| { |
| page()->setUrl(url); |
| } |
| |
| QUrl QWebEngineView::url() const |
| { |
| return page()->url(); |
| } |
| |
| QUrl QWebEngineView::iconUrl() const |
| { |
| return page()->iconUrl(); |
| } |
| |
| /*! |
| \property QWebEngineView::icon |
| \brief The icon associated with the page currently viewed. |
| \since 5.7 |
| |
| By default, this property contains a null icon. |
| |
| \sa iconChanged(), iconUrl(), iconUrlChanged() |
| */ |
| QIcon QWebEngineView::icon() const |
| { |
| return page()->icon(); |
| } |
| |
| bool QWebEngineView::hasSelection() const |
| { |
| return page()->hasSelection(); |
| } |
| |
| QString QWebEngineView::selectedText() const |
| { |
| return page()->selectedText(); |
| } |
| |
| #ifndef QT_NO_ACTION |
| QAction* QWebEngineView::pageAction(QWebEnginePage::WebAction action) const |
| { |
| return page()->action(action); |
| } |
| #endif |
| |
| void QWebEngineView::triggerPageAction(QWebEnginePage::WebAction action, bool checked) |
| { |
| page()->triggerAction(action, checked); |
| } |
| |
| void QWebEngineView::findText(const QString &subString, QWebEnginePage::FindFlags options, const QWebEngineCallback<bool> &resultCallback) |
| { |
| page()->findText(subString, options, resultCallback); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| QSize QWebEngineView::sizeHint() const |
| { |
| // TODO: Remove this override for Qt 6 |
| return QWidget::sizeHint(); |
| } |
| |
| QWebEngineSettings *QWebEngineView::settings() const |
| { |
| return page()->settings(); |
| } |
| |
| void QWebEngineView::stop() |
| { |
| page()->triggerAction(QWebEnginePage::Stop); |
| } |
| |
| void QWebEngineView::back() |
| { |
| page()->triggerAction(QWebEnginePage::Back); |
| } |
| |
| void QWebEngineView::forward() |
| { |
| page()->triggerAction(QWebEnginePage::Forward); |
| } |
| |
| void QWebEngineView::reload() |
| { |
| page()->triggerAction(QWebEnginePage::Reload); |
| } |
| |
| QWebEngineView *QWebEngineView::createWindow(QWebEnginePage::WebWindowType type) |
| { |
| Q_UNUSED(type) |
| return 0; |
| } |
| |
| qreal QWebEngineView::zoomFactor() const |
| { |
| return page()->zoomFactor(); |
| } |
| |
| void QWebEngineView::setZoomFactor(qreal factor) |
| { |
| page()->setZoomFactor(factor); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| bool QWebEngineView::event(QEvent *ev) |
| { |
| if (ev->type() == QEvent::ContextMenu) { |
| if (contextMenuPolicy() == Qt::NoContextMenu) { |
| // We forward the contextMenu event to the parent widget |
| ev->ignore(); |
| return false; |
| } |
| |
| // We swallow spontaneous contextMenu events and synthethize those back later on when we get the |
| // HandleContextMenu callback from chromium |
| ev->accept(); |
| return true; |
| } |
| |
| // Override QWidget's default ToolTip handler since it doesn't hide tooltip on empty text. |
| if (ev->type() == QEvent::ToolTip) { |
| if (!toolTip().isEmpty()) |
| QToolTip::showText(static_cast<QHelpEvent *>(ev)->globalPos(), toolTip(), this, QRect(), toolTipDuration()); |
| else |
| QToolTip::hideText(); |
| |
| ev->accept(); |
| return true; |
| } |
| |
| return QWidget::event(ev); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| #if QT_CONFIG(contextmenu) |
| void QWebEngineView::contextMenuEvent(QContextMenuEvent *event) |
| { |
| QMenu *menu = page()->createStandardContextMenu(); |
| menu->popup(event->globalPos()); |
| } |
| #endif // QT_CONFIG(contextmenu) |
| |
| /*! |
| * \reimp |
| */ |
| void QWebEngineView::showEvent(QShowEvent *event) |
| { |
| QWidget::showEvent(event); |
| page()->setVisible(true); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| void QWebEngineView::hideEvent(QHideEvent *event) |
| { |
| QWidget::hideEvent(event); |
| page()->setVisible(false); |
| } |
| |
| /*! |
| * \reimp |
| */ |
| void QWebEngineView::closeEvent(QCloseEvent *event) |
| { |
| QWidget::closeEvent(event); |
| page()->setVisible(false); |
| page()->setLifecycleState(QWebEnginePage::LifecycleState::Discarded); |
| } |
| |
| #if QT_CONFIG(draganddrop) |
| /*! |
| \reimp |
| */ |
| void QWebEngineView::dragEnterEvent(QDragEnterEvent *e) |
| { |
| Q_D(QWebEngineView); |
| e->accept(); |
| if (d->m_dragEntered) |
| d->page->d_ptr->adapter->leaveDrag(); |
| d->page->d_ptr->adapter->enterDrag(e, mapToGlobal(e->pos())); |
| d->m_dragEntered = true; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWebEngineView::dragLeaveEvent(QDragLeaveEvent *e) |
| { |
| Q_D(QWebEngineView); |
| if (!d->m_dragEntered) |
| return; |
| e->accept(); |
| d->page->d_ptr->adapter->leaveDrag(); |
| d->m_dragEntered = false; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWebEngineView::dragMoveEvent(QDragMoveEvent *e) |
| { |
| Q_D(QWebEngineView); |
| if (!d->m_dragEntered) |
| return; |
| QtWebEngineCore::WebContentsAdapter *adapter = d->page->d_ptr->adapter.data(); |
| Qt::DropAction dropAction = adapter->updateDragPosition(e, mapToGlobal(e->pos())); |
| if (Qt::IgnoreAction == dropAction) { |
| e->ignore(); |
| } else { |
| e->setDropAction(dropAction); |
| e->accept(); |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWebEngineView::dropEvent(QDropEvent *e) |
| { |
| Q_D(QWebEngineView); |
| if (!d->m_dragEntered) |
| return; |
| e->accept(); |
| d->page->d_ptr->adapter->endDragging(e, mapToGlobal(e->pos())); |
| d->m_dragEntered = false; |
| } |
| #endif // QT_CONFIG(draganddrop) |
| |
| #ifndef QT_NO_ACCESSIBILITY |
| int QWebEngineViewAccessible::childCount() const |
| { |
| if (view() && child(0)) |
| return 1; |
| return 0; |
| } |
| |
| QAccessibleInterface *QWebEngineViewAccessible::child(int index) const |
| { |
| if (index == 0 && view() && view()->page()) |
| return view()->page()->d_func()->adapter->browserAccessible(); |
| return nullptr; |
| } |
| |
| int QWebEngineViewAccessible::indexOfChild(const QAccessibleInterface *c) const |
| { |
| if (c == child(0)) |
| return 0; |
| return -1; |
| } |
| #endif // QT_NO_ACCESSIBILITY |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qwebengineview.cpp" |