summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Varga <pvarga@inf.u-szeged.hu>2016-07-13 17:28:15 +0200
committerPeter Varga <pvarga@inf.u-szeged.hu>2016-08-02 14:08:32 +0000
commit9b8c1020f2752ab5095086577ab98fa80926c43d (patch)
treea0780e2dd66d2dd3eeff727cf81d97c0632aa36a
parentea616d1047325fed629eb3bfef85550daff56ff7 (diff)
Add View Source API and make the feature available from context menu
[ChangeLog][QtWebEngineQML][QQuickWebEngineView] View Source feature is now supported [ChangeLog][QtWebEngineWidgets][QWebEnginePage] View Source feature is now supported Change-Id: Icc16da71fc6ec95880897fc9744dd8be8c004e00 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--examples/webenginewidgets/demobrowser/browsermainwindow.cpp19
-rw-r--r--examples/webenginewidgets/demobrowser/browsermainwindow.h1
-rw-r--r--src/core/web_contents_adapter.cpp12
-rw-r--r--src/core/web_contents_adapter.h3
-rw-r--r--src/webengine/api/qquickwebengineview.cpp16
-rw-r--r--src/webengine/api/qquickwebengineview_p.h3
-rw-r--r--src/webengine/doc/src/webengineview.qdoc17
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp46
-rw-r--r--src/webenginewidgets/api/qwebenginepage.h4
-rw-r--r--src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc1
-rw-r--r--tests/auto/quick/qmltests/data/tst_viewSource.qml93
-rw-r--r--tests/auto/quick/qmltests/qmltests.pro1
-rw-r--r--tests/auto/widgets/qwebenginepage/resources/test1.html7
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp21
14 files changed, 227 insertions, 17 deletions
diff --git a/examples/webenginewidgets/demobrowser/browsermainwindow.cpp b/examples/webenginewidgets/demobrowser/browsermainwindow.cpp
index 14ed10dfc..b93c538d8 100644
--- a/examples/webenginewidgets/demobrowser/browsermainwindow.cpp
+++ b/examples/webenginewidgets/demobrowser/browsermainwindow.cpp
@@ -403,7 +403,10 @@ void BrowserMainWindow::setupMenu()
viewMenu->addAction(tr("Reset &Zoom"), this, SLOT(slotViewResetZoom()), QKeySequence(Qt::CTRL | Qt::Key_0));
viewMenu->addSeparator();
- viewMenu->addAction(tr("Page S&ource"), this, SLOT(slotViewPageSource()), tr("Ctrl+Alt+U"));
+ QAction *m_pageSource = viewMenu->addAction(tr("Page S&ource"));
+ m_pageSource->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_U));
+ m_tabWidget->addWebAction(m_pageSource, QWebEnginePage::ViewSource);
+
QAction *a = viewMenu->addAction(tr("&Full Screen"), this, SLOT(slotViewFullScreen(bool)), Qt::Key_F11);
a->setCheckable(true);
@@ -849,20 +852,6 @@ void BrowserMainWindow::slotViewFullScreen(bool makeFullScreen)
}
}
-void BrowserMainWindow::slotViewPageSource()
-{
- if (!currentTab())
- return;
-
- QPlainTextEdit *view = new QPlainTextEdit;
- view->setWindowTitle(tr("Page Source of %1").arg(currentTab()->title()));
- view->setMinimumWidth(640);
- view->setAttribute(Qt::WA_DeleteOnClose);
- view->show();
-
- currentTab()->page()->toHtml(invoke(view, &QPlainTextEdit::setPlainText));
-}
-
void BrowserMainWindow::slotHome()
{
QSettings settings;
diff --git a/examples/webenginewidgets/demobrowser/browsermainwindow.h b/examples/webenginewidgets/demobrowser/browsermainwindow.h
index 9fb6b0851..7bd1ffaf1 100644
--- a/examples/webenginewidgets/demobrowser/browsermainwindow.h
+++ b/examples/webenginewidgets/demobrowser/browsermainwindow.h
@@ -123,7 +123,6 @@ private slots:
void slotViewToolbar();
void slotViewBookmarksBar();
void slotViewStatusbar();
- void slotViewPageSource();
void slotViewFullScreen(bool enable);
void slotWebSearch();
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 364d4d5b2..c3789318a 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1281,4 +1281,16 @@ FaviconManager *WebContentsAdapter::faviconManager()
return d->webContentsDelegate->faviconManager();
}
+void WebContentsAdapter::viewSource()
+{
+ Q_D(WebContentsAdapter);
+ d->webContents->ViewSource();
+}
+
+bool WebContentsAdapter::canViewSource()
+{
+ Q_D(WebContentsAdapter);
+ return d->webContents->GetController().CanViewSource();
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 63b99d013..72e1f6447 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -178,6 +178,9 @@ public:
content::WebContents *webContents() const;
void replaceMisspelling(const QString &word);
+ void viewSource();
+ bool canViewSource();
+
private:
Q_DISABLE_COPY(WebContentsAdapter)
Q_DECLARE_PRIVATE(WebContentsAdapter)
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 983e8c30e..0170018e7 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -240,6 +240,10 @@ bool QQuickWebEngineViewPrivate::contextMenuRequested(const WebEngineContextMenu
item = new MenuItemHandler(menu);
QObject::connect(item, &MenuItemHandler::triggered, q, &QQuickWebEngineView::reload);
ui()->addMenuItem(item, QQuickWebEngineView::tr("Reload"), QStringLiteral("view-refresh"));
+
+ item = new MenuItemHandler(menu);
+ QObject::connect(item, &MenuItemHandler::triggered, q, &QQuickWebEngineView::viewSource);
+ ui()->addMenuItem(item, QQuickWebEngineView::tr("View Page Source"), QStringLiteral("view-source"), q->canViewSource());
} else {
item = new MenuItemHandler(menu);
QObject::connect(item, &MenuItemHandler::triggered, [q] { q->triggerWebAction(QQuickWebEngineView::Copy); });
@@ -1278,6 +1282,18 @@ void QQuickWebEngineView::replaceMisspelledWord(const QString &replacement)
d->adapter->replaceMisspelling(replacement);
}
+void QQuickWebEngineView::viewSource()
+{
+ Q_D(QQuickWebEngineView);
+ d->adapter->viewSource();
+}
+
+bool QQuickWebEngineView::canViewSource() const
+{
+ Q_D(const QQuickWebEngineView);
+ return d->adapter->canViewSource();
+}
+
bool QQuickWebEngineView::isFullScreen() const
{
Q_D(const QQuickWebEngineView);
diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h
index 367497da5..dc693a94c 100644
--- a/src/webengine/api/qquickwebengineview_p.h
+++ b/src/webengine/api/qquickwebengineview_p.h
@@ -119,6 +119,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem {
Q_PROPERTY(bool audioMuted READ isAudioMuted WRITE setAudioMuted NOTIFY audioMutedChanged FINAL REVISION 3)
Q_PROPERTY(bool recentlyAudible READ recentlyAudible NOTIFY recentlyAudibleChanged FINAL REVISION 3)
Q_PROPERTY(uint webChannelWorld READ webChannelWorld WRITE setWebChannelWorld NOTIFY webChannelWorldChanged REVISION 3)
+ Q_PROPERTY(bool canViewSource READ canViewSource FINAL REVISION 4)
#ifdef ENABLE_QML_TESTSUPPORT_API
Q_PROPERTY(QQuickWebEngineTestSupport *testSupport READ testSupport WRITE setTestSupport FINAL)
@@ -145,6 +146,7 @@ public:
void setBackgroundColor(const QColor &color);
QSizeF contentsSize() const;
QPointF scrollPosition() const;
+ bool canViewSource() const;
QQuickWebEngineViewExperimental *experimental() const;
@@ -473,6 +475,7 @@ public Q_SLOTS:
Q_REVISION(3) void printToPdf(const QString &filePath, PrintedPageSizeId pageSizeId = PrintedPageSizeId::A4, PrintedPageOrientation orientation = PrintedPageOrientation::Portrait);
Q_REVISION(3) void printToPdf(const QJSValue &callback, PrintedPageSizeId pageSizeId = PrintedPageSizeId::A4, PrintedPageOrientation orientation = PrintedPageOrientation::Portrait);
Q_REVISION(4) void replaceMisspelledWord(const QString &replacement);
+ Q_REVISION(4) void viewSource();
private Q_SLOTS:
void lazyInitialize();
diff --git a/src/webengine/doc/src/webengineview.qdoc b/src/webengine/doc/src/webengineview.qdoc
index 0f83d0d23..83eadd563 100644
--- a/src/webengine/doc/src/webengineview.qdoc
+++ b/src/webengine/doc/src/webengineview.qdoc
@@ -1128,3 +1128,20 @@
Also if the audio is paused, this signal is emitted with an approximate \b{2 second
delay}, from the moment the audio is paused.
*/
+
+/*!
+ \qmlmethod void WebEngineView::viewSource()
+ \since QtWebEngine 1.4
+
+ Shows the source of the current page in a new tab.
+
+ \sa canViewSource
+*/
+
+/*!
+ \qmlproperty bool WebEngineView::canViewSource
+ \brief This property holds whether the source for the current page can be viewed.
+ \since QtWebEngine 1.4
+
+ \sa viewSource()
+*/
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 3f2323334..be5a39e83 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -81,6 +81,7 @@
#include <QMimeData>
#include <QStandardPaths>
#include <QStyle>
+#include <QTimer>
#include <QUrl>
QT_BEGIN_NAMESPACE
@@ -422,6 +423,9 @@ void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const
case QWebEnginePage::ReloadAndBypassCache:
enabled = !isLoading;
break;
+ case QWebEnginePage::ViewSource:
+ enabled = adapter->canViewSource();
+ break;
default:
break;
}
@@ -437,6 +441,7 @@ void QWebEnginePagePrivate::updateNavigationActions()
updateAction(QWebEnginePage::Stop);
updateAction(QWebEnginePage::Reload);
updateAction(QWebEnginePage::ReloadAndBypassCache);
+ updateAction(QWebEnginePage::ViewSource);
}
#ifndef QT_NO_ACTION
@@ -931,6 +936,9 @@ QAction *QWebEnginePage::action(WebAction action) const
case SavePage:
text = tr("Save &Page");
break;
+ case ViewSource:
+ text = tr("&View Page Source");
+ break;
case NoWebAction:
case WebActionCount:
Q_UNREACHABLE();
@@ -1121,6 +1129,17 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
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 NoWebAction:
break;
case WebActionCount:
@@ -1378,6 +1397,8 @@ QMenu *QWebEnginePage::createStandardContextMenu()
action = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), tr("&Reload"), menu);
connect(action, &QAction::triggered, d->view, &QWebEngineView::reload);
menu->addAction(action);
+
+ menu->addAction(QWebEnginePage::action(ViewSource));
} else {
menu->addAction(QWebEnginePage::action(Copy));
menu->addAction(QWebEnginePage::action(Unselect));
@@ -1788,6 +1809,31 @@ const QWebEngineContextMenuData &QWebEnginePage::contextMenuData() const
return d->contextData;
}
+/*!
+ \since 5.8
+
+ Shows the source of the current page in a new tab.
+
+ \sa canViewSource
+*/
+void QWebEnginePage::viewSource()
+{
+ triggerAction(QWebEnginePage::ViewSource);
+}
+
+/*!
+ \property QWebEnginePage::canViewSource
+ \brief whether the source for the current page can be viewed.
+ \since 5.8
+
+ \sa viewSource()
+*/
+bool QWebEnginePage::canViewSource() const
+{
+ Q_D(const QWebEnginePage);
+ return d->adapter->canViewSource();
+}
+
QT_END_NAMESPACE
#include "moc_qwebenginepage.cpp"
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index ad32f169d..bb46cd9fb 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -81,6 +81,7 @@ class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject {
Q_PROPERTY(QPointF scrollPosition READ scrollPosition NOTIFY scrollPositionChanged)
Q_PROPERTY(bool audioMuted READ isAudioMuted WRITE setAudioMuted NOTIFY audioMutedChanged)
Q_PROPERTY(bool recentlyAudible READ recentlyAudible NOTIFY recentlyAudibleChanged)
+ Q_PROPERTY(bool canViewSource READ canViewSource)
public:
enum WebAction {
@@ -124,6 +125,7 @@ public:
Unselect,
SavePage,
OpenLinkInNewBackgroundTab,
+ ViewSource,
WebActionCount
};
@@ -279,6 +281,8 @@ public:
#endif
const QWebEngineContextMenuData &contextMenuData() const;
+ void viewSource();
+ bool canViewSource() const;
Q_SIGNALS:
void loadStarted();
diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
index 539e809d4..a0dc49d0a 100644
--- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
+++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
@@ -152,6 +152,7 @@
\value Unselect Clear the current selection. (Added in Qt 5.7)
\value SavePage Save the current page to disk. MHTML is the default format that is used to store
the web page on disk. (Added in Qt 5.7)
+ \value ViewSource Shows the source of the current page in a new tab. (Added in Qt 5.8)
\omitvalue WebActionCount
diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml
new file mode 100644
index 000000000..7c7dd34e0
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtTest 1.0
+import QtWebEngine 1.4
+
+TestWebEngineView {
+ id: webEngineView
+ width: 200
+ height: 400
+
+ property var viewRequest: null
+
+ SignalSpy {
+ id: newViewRequestedSpy
+ target: webEngineView
+ signalName: "newViewRequested"
+ }
+
+ SignalSpy {
+ id: titleChangedSpy
+ target: webEngineView
+ signalName: "titleChanged"
+ }
+
+ onNewViewRequested: {
+ viewRequest = {
+ "destination": request.destination,
+ "userInitiated": request.userInitiated
+ };
+
+ request.openIn(webEngineView);
+ }
+
+ TestCase {
+ id: test
+ name: "WebEngineViewSource"
+
+ function init() {
+ newViewRequestedSpy.clear();
+ titleChangedSpy.clear();
+ viewRequest = null;
+ }
+
+ function test_viewSource() {
+ webEngineView.url = Qt.resolvedUrl("test1.html");
+ verify(webEngineView.waitForLoadSucceeded());
+ compare(webEngineView.title, "Test page 1");
+ verify(webEngineView.canViewSource, true);
+
+ titleChangedSpy.clear();
+ webEngineView.viewSource();
+ tryCompare(newViewRequestedSpy, "count", 1);
+ verify(webEngineView.waitForLoadSucceeded());
+ // The first titleChanged signal is emitted by adoptWebContents()
+ tryCompare(titleChangedSpy, "count", 2);
+
+ compare(viewRequest.destination, WebEngineView.NewViewInTab);
+ verify(viewRequest.userInitiated);
+ verify(!webEngineView.canViewSource);
+
+ compare(webEngineView.title, "test1.html");
+ compare(webEngineView.url, "view-source:" + Qt.resolvedUrl("test1.html"));
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro
index 0158a7268..abb0a86fb 100644
--- a/tests/auto/quick/qmltests/qmltests.pro
+++ b/tests/auto/quick/qmltests/qmltests.pro
@@ -63,6 +63,7 @@ OTHER_FILES += \
$$PWD/data/tst_titleChanged.qml \
$$PWD/data/tst_unhandledKeyEventPropagation.qml \
$$PWD/data/tst_userScripts.qml \
+ $$PWD/data/tst_viewSource.qml \
$$PWD/data/tst_webchannel.qml \
$$PWD/data/tst_settings.qml \
$$PWD/data/tst_keyboardModifierMapping.qml \
diff --git a/tests/auto/widgets/qwebenginepage/resources/test1.html b/tests/auto/widgets/qwebenginepage/resources/test1.html
index b323f966e..5c09f06ed 100644
--- a/tests/auto/widgets/qwebenginepage/resources/test1.html
+++ b/tests/auto/widgets/qwebenginepage/resources/test1.html
@@ -1 +1,6 @@
-<html><body><p>Some text 1</p></body></html>
+<html>
+<head><title>Test page 1</title></head>
+<body>
+Hello.
+</body>
+</html>
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 697f45a83..874581f1c 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -239,6 +239,7 @@ private Q_SLOTS:
void mouseButtonTranslation();
void printToPdf();
+ void viewSource();
private:
QWebEngineView* m_view;
@@ -4938,5 +4939,25 @@ void tst_QWebEnginePage::mouseButtonTranslation()
delete view;
}
+void tst_QWebEnginePage::viewSource()
+{
+ TestPage page;
+ QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+ const QUrl url("qrc:/resources/test1.html");
+
+ page.load(url);
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QCOMPARE(page.title(), QStringLiteral("Test page 1"));
+ QVERIFY(page.canViewSource());
+
+ page.viewSource();
+ QTest::qWait(200);
+ QTRY_COMPARE(page.createdWindows.size(), 1);
+
+ QTRY_COMPARE(page.createdWindows[0]->url().toString(), QStringLiteral("view-source:%1").arg(url.toString()));
+ QTRY_COMPARE(page.createdWindows[0]->title(), QStringLiteral("view-source:%1").arg(url.toString()));
+ QVERIFY(!page.createdWindows[0]->canViewSource());
+}
+
QTEST_MAIN(tst_QWebEnginePage)
#include "tst_qwebenginepage.moc"