An error occurred while loading the file. Please try again.
-
Sergio Ahumada authored
Change-Id: Ic804938fc352291d011800d21e549c10acac66fb Reviewed-by:
Lars Knoll <lars.knoll@digia.com>
48e0c4df
/****************************************************************************
**
** 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 "qwebenginepage.h"
#include "qwebenginepage_p.h"
#include "authentication_dialog_controller.h"
#include "browser_context_adapter.h"
#include "certificate_error_controller.h"
#include "color_chooser_controller.h"
#include "favicon_manager.h"
#include "file_picker_controller.h"
#include "javascript_dialog_controller.h"
#include "qwebenginefullscreenrequest.h"
#include "qwebenginehistory.h"
#include "qwebenginehistory_p.h"
#include "qwebengineprofile.h"
#include "qwebengineprofile_p.h"
#include "qwebenginescriptcollection_p.h"
#include "qwebenginesettings.h"
#include "qwebengineview.h"
#include "qwebengineview_p.h"
#include "render_widget_host_view_qt_delegate_widget.h"
#include "web_contents_adapter.h"
#include "web_engine_settings.h"
#ifdef QT_UI_DELEGATES
#include "ui/messagebubblewidget_p.h"
#endif
#include <QAction>
#include <QApplication>
#include <QAuthenticator>
#include <QClipboard>
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
#include <QColorDialog>
#include <QContextMenuEvent>
#include <QFileDialog>
#include <QKeyEvent>
#include <QIcon>
#include <QInputDialog>
#include <QLayout>
#include <QLoggingCategory>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QStandardPaths>
#include <QStyle>
#include <QUrl>
QT_BEGIN_NAMESPACE
using namespace QtWebEngineCore;
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();
}
}
QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile)
: adapter(new WebContentsAdapter)
, history(new QWebEngineHistory(new QWebEngineHistoryPrivate(this)))
, profile(_profile ? _profile : QWebEngineProfile::defaultProfile())
, settings(new QWebEngineSettings(profile->settings()))
, view(0)
, isLoading(false)
, scriptCollection(new QWebEngineScriptCollectionPrivate(browserContextAdapter()->userResourceController(), adapter.data()))
, m_isBeingAdopted(false)
, m_backgroundColor(Qt::white)
, fullscreenMode(false)
, webChannel(nullptr)
, webChannelWorldId(QWebEngineScript::MainWorld)
{
memset(actions, 0, sizeof(actions));
}
QWebEnginePagePrivate::~QWebEnginePagePrivate()
{
delete history;
delete settings;
}
RenderWidgetHostViewQtDelegate *QWebEnginePagePrivate::CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client)
{
return new RenderWidgetHostViewQtDelegateWidget(client);
}
void QWebEnginePagePrivate::titleChanged(const QString &title)
{
Q_Q(QWebEnginePage);
Q_EMIT q->titleChanged(title);
}
void QWebEnginePagePrivate::urlChanged(const QUrl &url)
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
{
Q_Q(QWebEnginePage);
explicitUrl = QUrl();
Q_EMIT q->urlChanged(url);
}
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);
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);
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();
}
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);
Q_Q(QWebEnginePage);
if (isErrorPage)
return;
isLoading = true;
Q_EMIT q->loadStarted();
updateNavigationActions();
}
void QWebEnginePagePrivate::loadCommitted()
{
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
updateNavigationActions();
}
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));
Q_ASSERT(success);
Q_EMIT q->loadFinished(false);
return;
}
isLoading = false;
if (success)
explicitUrl = QUrl();
// 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)) {
Q_EMIT q->loadFinished(success);
}
updateNavigationActions();
}
void QWebEnginePagePrivate::focusContainer()
{
if (view)
view->setFocus();
}
void QWebEnginePagePrivate::unhandledKeyEvent(QKeyEvent *event)
{
if (view && view->parentWidget())
QGuiApplication::sendEvent(view->parentWidget(), event);
}
void QWebEnginePagePrivate::adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry)
{
Q_Q(QWebEnginePage);
Q_UNUSED(userGesture);
QWebEnginePage *newPage = q->createWindow(toWindowType(disposition));
if (!newPage)
return;
// 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.
if (newPage->d_func() != this) {
newPage->d_func()->adapter = newWebContents;
newWebContents->initialize(newPage->d_func());
if (!initialGeometry.isEmpty())
emit newPage->geometryChangeRequested(initialGeometry);
}
// Page has finished the adoption process.
newPage->d_func()->m_isBeingAdopted = false;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
}
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::didFindText(quint64 requestId, int matchCount)
{
m_callbacks.invoke(requestId, matchCount > 0);
}
void QWebEnginePagePrivate::didPrintPage(quint64 requestId, const QByteArray &result)
{
m_callbacks.invoke(requestId, result);
}
void QWebEnginePagePrivate::passOnFocus(bool reverse)
{
if (view)
view->focusNextPrevChild(!reverse);
}
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::showColorDialog(QSharedPointer<ColorChooserController> controller)
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
{
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();
}
void QWebEnginePagePrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags)
{
Q_Q(QWebEnginePage);
QWebEnginePage::Feature requestedFeature;
if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
requestedFeature = QWebEnginePage::MediaAudioVideoCapture;
else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
requestedFeature = QWebEnginePage::MediaAudioCapture;
else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
requestedFeature = QWebEnginePage::MediaVideoCapture;
else
return;
Q_EMIT q->featurePermissionRequested(securityOrigin, requestedFeature);
}
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);
}
#ifndef QT_NO_ACCESSIBILITY
QObject *QWebEnginePagePrivate::accessibilityParentObject()
{
return view;
}
#endif // QT_NO_ACCESSIBILITY
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->canGoBack();
break;
case QWebEnginePage::Forward:
enabled = adapter->canGoForward();
break;
case QWebEnginePage::Stop:
enabled = isLoading;
break;
case QWebEnginePage::Reload:
case QWebEnginePage::ReloadAndBypassCache:
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
enabled = !isLoading;
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);
}
#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)
{
QExplicitlySharedDataPointer<WebContentsAdapter> newWebContents = WebContentsAdapter::createFromSerializedNavigationHistory(input, this);
if (newWebContents) {
// Keep the old adapter referenced so the user-scripts are not
// unregistered immediately.
QExplicitlySharedDataPointer<WebContentsAdapter> oldWebContents = adapter;
adapter = newWebContents.data();
adapter->initialize(this);
if (webChannel)
adapter->setWebChannel(webChannel, webChannelWorldId);
scriptCollection.d->rebindToContents(adapter.data());
}
}
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();
}
}
QSharedPointer<BrowserContextAdapter> QWebEnginePagePrivate::browserContextAdapter()
{
return profile->d_ptr->browserContext();
}
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
WebContentsAdapter *QWebEnginePagePrivate::webContentsAdapter()
{
return adapter.data();
}
QWebEnginePage::QWebEnginePage(QObject* parent)
: QObject(parent)
, d_ptr(new QWebEnginePagePrivate())
{
Q_D(QWebEnginePage);
d->q_ptr = this;
d->adapter->initialize(d);
}
/*!
\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 request)
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 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
*/
/*!
\property QWebEnginePage::scrollPosition
\since 5.7
\brief The scroll position of the page contents.
*/
/*!
\property QWebEnginePage::contentsSize
\since 5.7
The size of the page contents.
*/
/*!
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
\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->initialize(d);
}
QWebEnginePage::~QWebEnginePage()
{
Q_D(QWebEnginePage);
QWebEngineViewPrivate::bind(d->view, 0);
}
QWebEngineHistory *QWebEnginePage::history() const
{
Q_D(const QWebEnginePage);
return d->history;
}
QWebEngineSettings *QWebEnginePage::settings() const
{
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
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
{
Q_D(const QWebEnginePage);
return d->webChannel;
}
/*!
* \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)
{
Q_D(QWebEnginePage);
if (d->webChannel != channel || d->webChannelWorldId != worldId) {
d->webChannel = channel;
d->webChannelWorldId = worldId;
d->adapter->setWebChannel(channel, worldId);
}
}
/*!
\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.