From 5555afd6ce01bc9891ee33b075cc53f4fa48665e Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Sat, 5 Sep 2015 19:14:17 -0700 Subject: Emit a signal when the rendering process exits. This allows users to implement a "sad tab" feature and/or track rendering process crashes using a crash reporting service. Task-number: QTBUG-48227 Change-Id: I97ef934fe5d0912cd0f41967a39052316b3c66b0 Reviewed-by: Allan Sandfeld Jensen --- .../webengine/quicknanobrowser/BrowserWindow.qml | 29 ++++++++++++++++++++ .../webenginewidgets/demobrowser/demobrowser.pro | 1 + examples/webenginewidgets/demobrowser/webview.cpp | 22 +++++++++++++++ src/core/render_widget_host_view_qt.cpp | 5 +++- src/core/web_contents_adapter.cpp | 32 ++++++++++++++++++++++ src/core/web_contents_adapter_client.h | 9 ++++++ src/webengine/api/qquickwebengineview.cpp | 8 ++++++ src/webengine/api/qquickwebengineview_p.h | 10 +++++++ src/webengine/api/qquickwebengineview_p_p.h | 2 ++ src/webenginewidgets/api/qwebenginepage.cpp | 8 ++++++ src/webenginewidgets/api/qwebenginepage.h | 10 +++++++ src/webenginewidgets/api/qwebenginepage_p.h | 2 ++ src/webenginewidgets/api/qwebengineview.cpp | 1 + src/webenginewidgets/api/qwebengineview.h | 2 ++ 14 files changed, 140 insertions(+), 1 deletion(-) diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index bdee68d17..4d7513fae 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -393,6 +393,35 @@ ApplicationWindow { } request.accept() } + + onRenderProcessTerminated: { + var status = "" + switch (terminationStatus) { + case WebEngineView.NormalTerminationStatus: + status = "(normal exit)" + break; + case WebEngineView.AbnormalTerminationStatus: + status = "(abnormal exit)" + break; + case WebEngineView.CrashedTerminationStatus: + status = "(crashed)" + break; + case WebEngineView.KilledTerminationStatus: + status = "(killed)" + break; + } + + print("Render process exited with code " + exitCode + " " + status) + reloadTimer.running = true + } + + Timer { + id: reloadTimer + interval: 0 + running: false + repeat: false + onTriggered: currentWebView.reload() + } } } } diff --git a/examples/webenginewidgets/demobrowser/demobrowser.pro b/examples/webenginewidgets/demobrowser/demobrowser.pro index 310aea59c..0893fe649 100644 --- a/examples/webenginewidgets/demobrowser/demobrowser.pro +++ b/examples/webenginewidgets/demobrowser/demobrowser.pro @@ -1,6 +1,7 @@ TEMPLATE = app TARGET = demobrowser QT += webenginewidgets network widgets printsupport +CONFIG += c++11 qtHaveModule(uitools):!embedded: QT += uitools else: DEFINES += QT_NO_UITOOLS diff --git a/examples/webenginewidgets/demobrowser/webview.cpp b/examples/webenginewidgets/demobrowser/webview.cpp index e454f06c4..c12f3db36 100644 --- a/examples/webenginewidgets/demobrowser/webview.cpp +++ b/examples/webenginewidgets/demobrowser/webview.cpp @@ -66,6 +66,7 @@ #include #include +#include WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) @@ -341,6 +342,27 @@ WebView::WebView(QWidget* parent) this, SLOT(setProgress(int))); connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); + connect(this, &QWebEngineView::renderProcessTerminated, + [=](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) { + const char *status = ""; + switch (termStatus) { + case QWebEnginePage::NormalTerminationStatus: + status = "(normal exit)"; + break; + case QWebEnginePage::AbnormalTerminationStatus: + status = "(abnormal exit)"; + break; + case QWebEnginePage::CrashedTerminationStatus: + status = "(crashed)"; + break; + case QWebEnginePage::KilledTerminationStatus: + status = "(killed)"; + break; + } + + qInfo() << "Render process exited with code" << statusCode << status; + QTimer::singleShot(0, [this] { reload(); }); + }); } void WebView::setPage(WebPage *_page) diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 7ca528954..26ea4f4ae 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -567,8 +567,11 @@ void RenderWidgetHostViewQt::ImeCompositionRangeChanged(const gfx::Range&, const QT_NOT_YET_IMPLEMENTED } -void RenderWidgetHostViewQt::RenderProcessGone(base::TerminationStatus, int) +void RenderWidgetHostViewQt::RenderProcessGone(base::TerminationStatus terminationStatus, + int exitCode) { + m_adapterClient->renderProcessTerminated( + m_adapterClient->renderProcessExitStatus(terminationStatus), exitCode); Destroy(); } diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 6fb2f5c97..58fd23969 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -900,4 +900,36 @@ void WebContentsAdapter::setWebChannel(QWebChannel *channel) channel->connectTo(d->webChannelTransport.get()); } +WebContentsAdapterClient::RenderProcessTerminationStatus +WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) { + auto status = WebContentsAdapterClient::RenderProcessTerminationStatus(-1); + switch (terminationStatus) { + case base::TERMINATION_STATUS_NORMAL_TERMINATION: + status = WebContentsAdapterClient::NormalTerminationStatus; + break; + case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: + status = WebContentsAdapterClient::AbnormalTerminationStatus; + break; + case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: +#if defined(OS_CHROMEOS) + case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: +#endif + status = WebContentsAdapterClient::KilledTerminationStatus; + break; + case base::TERMINATION_STATUS_PROCESS_CRASHED: +#if defined(OS_ANDROID) + case base::TERMINATION_STATUS_OOM_PROTECTED: +#endif + status = WebContentsAdapterClient::CrashedTerminationStatus; + break; + case base::TERMINATION_STATUS_STILL_RUNNING: + case base::TERMINATION_STATUS_MAX_ENUM: + // should be unreachable since Chromium asserts status != TERMINATION_STATUS_STILL_RUNNING + // before calling this method + break; + } + + return status; +} + } // namespace QtWebEngineCore diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 8166224a4..3ae84f9c8 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -172,6 +172,13 @@ public: Error }; + enum RenderProcessTerminationStatus { + NormalTerminationStatus = 0, + AbnormalTerminationStatus, + CrashedTerminationStatus, + KilledTerminationStatus + }; + enum MediaRequestFlag { MediaNone = 0, MediaAudioCapture = 0x01, @@ -225,6 +232,8 @@ public: virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) = 0; virtual void hideValidationMessage() = 0; virtual void moveValidationMessage(const QRect &anchor) = 0; + RenderProcessTerminationStatus renderProcessExitStatus(int); + virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) = 0; virtual void allowCertificateError(const QSharedPointer &errorController) = 0; diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 83a6f68bb..b22b2798e 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -893,6 +893,14 @@ void QQuickWebEngineViewPrivate::moveValidationMessage(const QRect &anchor) ui()->moveMessageBubble(anchor); } +void QQuickWebEngineViewPrivate::renderProcessTerminated( + RenderProcessTerminationStatus terminationStatus, int exitCode) +{ + Q_Q(QQuickWebEngineView); + Q_EMIT q->renderProcessTerminated(static_cast( + renderProcessExitStatus(terminationStatus)), exitCode); +} + bool QQuickWebEngineView::isLoading() const { Q_D(const QQuickWebEngineView); diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index ab2e6a566..22cca3277 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -116,6 +116,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem { Q_ENUMS(NewViewDestination); Q_ENUMS(Feature); Q_ENUMS(JavaScriptConsoleMessageLevel); + Q_ENUMS(RenderProcessTerminationStatus); Q_FLAGS(FindFlags); Q_ENUMS(WebAction); @@ -236,6 +237,14 @@ public: ErrorMessageLevel }; + // must match WebContentsAdapterClient::RenderProcessTerminationStatus + enum RenderProcessTerminationStatus { + NormalTerminationStatus = 0, + AbnormalTerminationStatus, + CrashedTerminationStatus, + KilledTerminationStatus + }; + enum FindFlag { FindBackward = 1, FindCaseSensitively = 2, @@ -295,6 +304,7 @@ Q_SIGNALS: Q_REVISION(1) void webChannelChanged(); Q_REVISION(2) void activeFocusOnPressChanged(bool); Q_REVISION(2) void backgroundColorChanged(); + Q_REVISION(2) void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 4aebb6f7c..0140111b9 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -167,6 +167,8 @@ public: virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) Q_DECL_OVERRIDE; virtual void hideValidationMessage() Q_DECL_OVERRIDE; virtual void moveValidationMessage(const QRect &anchor) Q_DECL_OVERRIDE; + virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, + int exitCode) Q_DECL_OVERRIDE; virtual QtWebEngineCore::BrowserContextAdapter *browserContextAdapter() Q_DECL_OVERRIDE; diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index b6880b086..80baf1c74 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -921,6 +921,14 @@ void QWebEnginePagePrivate::moveValidationMessage(const QRect &anchor) #endif } +void QWebEnginePagePrivate::renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, + int exitCode) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->renderProcessTerminated(static_cast( + terminationStatus), exitCode); +} + QMenu *QWebEnginePage::createStandardContextMenu() { Q_D(QWebEnginePage); diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index d027998ee..90fa62f97 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -165,6 +165,14 @@ public: ErrorMessageLevel }; + // must match WebContentsAdapterClient::RenderProcessTerminationStatus + enum RenderProcessTerminationStatus { + NormalTerminationStatus = 0, + AbnormalTerminationStatus, + CrashedTerminationStatus, + KilledTerminationStatus + }; + explicit QWebEnginePage(QObject *parent = 0); QWebEnginePage(QWebEngineProfile *profile, QObject *parent = 0); ~QWebEnginePage(); @@ -247,6 +255,8 @@ Q_SIGNALS: void authenticationRequired(const QUrl &requestUrl, QAuthenticator *authenticator); void proxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *authenticator, const QString &proxyHost); + void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode); + // Ex-QWebFrame signals void titleChanged(const QString &title); void urlChanged(const QUrl &url); diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index a72debee2..8d9bc2517 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -119,6 +119,8 @@ public: virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) Q_DECL_OVERRIDE; virtual void hideValidationMessage() Q_DECL_OVERRIDE; virtual void moveValidationMessage(const QRect &anchor) Q_DECL_OVERRIDE; + virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, + int exitCode) Q_DECL_OVERRIDE; virtual QtWebEngineCore::BrowserContextAdapter *browserContextAdapter() Q_DECL_OVERRIDE; diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp index a4a8dd760..ddd1e4cbf 100644 --- a/src/webenginewidgets/api/qwebengineview.cpp +++ b/src/webenginewidgets/api/qwebengineview.cpp @@ -86,6 +86,7 @@ void QWebEngineViewPrivate::bind(QWebEngineView *view, QWebEnginePage *page) QObject::connect(page, &QWebEnginePage::loadProgress, view, &QWebEngineView::loadProgress); QObject::connect(page, &QWebEnginePage::loadFinished, view, &QWebEngineView::loadFinished); QObject::connect(page, &QWebEnginePage::selectionChanged, view, &QWebEngineView::selectionChanged); + QObject::connect(page, &QWebEnginePage::renderProcessTerminated, view, &QWebEngineView::renderProcessTerminated); } } diff --git a/src/webenginewidgets/api/qwebengineview.h b/src/webenginewidgets/api/qwebengineview.h index 0d8f9fc17..ae38e6c5c 100644 --- a/src/webenginewidgets/api/qwebengineview.h +++ b/src/webenginewidgets/api/qwebengineview.h @@ -113,6 +113,8 @@ Q_SIGNALS: void selectionChanged(); void urlChanged(const QUrl&); void iconUrlChanged(const QUrl&); + void renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, + int exitCode); protected: virtual QWebEngineView *createWindow(QWebEnginePage::WebWindowType type); -- cgit v1.2.3