Merge lp:~abreu-alexandre/webbrowser-app/handle-webapp-renderer-crash into lp:webbrowser-app

Proposed by Alexandre Abreu
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1215
Merged at revision: 1217
Proposed branch: lp:~abreu-alexandre/webbrowser-app/handle-webapp-renderer-crash
Merge into: lp:webbrowser-app
Diff against target: 434 lines (+291/-22)
6 files modified
src/app/webcontainer/CMakeLists.txt (+4/-0)
src/app/webcontainer/PopupWindowOverlay.qml (+17/-1)
src/app/webcontainer/SadPage.qml (+56/-0)
src/app/webcontainer/WebApp.qml (+41/-21)
tests/autopilot/webapp_container/tests/__init__.py (+11/-0)
tests/autopilot/webapp_container/tests/test_sad_tab.py (+162/-0)
To merge this branch: bzr merge lp:~abreu-alexandre/webbrowser-app/handle-webapp-renderer-crash
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Olivier Tilloy Approve
Review via email: mp+271865@code.launchpad.net

Commit message

Handle webapp webview renderer crashes

Description of the change

Handle webapp webview renderer crashes

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Olivier Tilloy (osomon) wrote :

Has this been signed off by the design team? I mentioned to them last week that you were going to implement something similar to what exists in the browser for the webapp container, and they agreed that the wording might require some tweaking. The mention of "tabs" in the default sad tab message that is used for the browser might be misleading in the context of the webapp container, especially if no overlay is currently being displayed.

One tiny remark: the SadTab autopilot proxy object should be defined in test_sad_tab.py, since it’s not used anywhere else.

Other than that, this looks good to me and seems to work well.

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

> One tiny remark: the SadTab autopilot proxy object should be defined in
> test_sad_tab.py, since it’s not used anywhere else.

updated

> Other than that, this looks good to me and seems to work well.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1199. By CI Train Bot Account

Resync trunk.

1200. By Olivier Tilloy

Update translation template.

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

I updated the UI (and obviously the rest) to simplify it and remove the user message so that it can land in OTA-7. Once it lands I'll create a ubuntu-ux task to verify and tweak that behavior (with a more meaningful message),

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1201. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1202. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1203. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1204. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1205. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1206. By Michael Sheldon

Add support for alternative mimetype header for vcards Fixes: #1498992
Approved by: Olivier Tilloy

1207. By Ugo Riboni

Add a context menu to each tab in the tab bar, allowing to insert a new tab just after, close or reload the current tab.
Approved by: Riccardo Padovani

1208. By Olivier Tilloy

Fix a couple of autopilot test failures on desktop. Fixes: #1495297, #1499411

1209. By CI Train Bot Account

Releasing 0.23+15.10.20150928-0ubuntu1

1210. By CI Train Bot Account

Resync trunk.

Revision history for this message
Olivier Tilloy (osomon) wrote :

Let’s discuss this proposed implementation at our weekly catchup with design later today.

A side note: "SadWebview.qml" is a confusing name, given that it’s not a webview.

1211. By Ugo Riboni

Extend the clickable area to close a tab on mobile, as taps are less precise and often end up missing it. Fixes: #1500339
Approved by: Olivier Tilloy

1212. By Olivier Tilloy

Use the UA override for www.youtube.com on mobile too. Fixes: #1499394
Approved by: Alexandre Abreu, David Barth

1213. By CI Train Bot Account

Releasing 0.23+15.10.20150929-0ubuntu1

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

Updated according to design's comment; also changed the SadWebview qml name

1214. By Alexandre Abreu

Handle renderer crash in webapp container

1215. By Alexandre Abreu

remove uneeded signal

Revision history for this message
Olivier Tilloy (osomon) wrote :

LGTM.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/webcontainer/CMakeLists.txt'
2--- src/app/webcontainer/CMakeLists.txt 2015-07-20 14:20:37 +0000
3+++ src/app/webcontainer/CMakeLists.txt 2015-09-29 20:15:01 +0000
4@@ -44,3 +44,7 @@
5 install(FILES ${QML_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webcontainer)
6 install(DIRECTORY actions DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webcontainer
7 FILES_MATCHING PATTERN *.qml)
8+
9+install(DIRECTORY assets
10+ DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webcontainer
11+ FILES_MATCHING PATTERN *.png)
12
13=== modified file 'src/app/webcontainer/PopupWindowOverlay.qml'
14--- src/app/webcontainer/PopupWindowOverlay.qml 2015-08-10 15:33:43 +0000
15+++ src/app/webcontainer/PopupWindowOverlay.qml 2015-09-29 20:15:01 +0000
16@@ -27,6 +27,7 @@
17
18 property var popupWindowController
19 property var webContext
20+ property alias currentWebview: popupWebview
21 property alias request: popupWebview.request
22 property alias url: popupWebview.url
23
24@@ -194,6 +195,21 @@
25 popupWindowController.handleViewRemoved(popup)
26 }
27 }
28+
29+ Loader {
30+ anchors {
31+ fill: popupWebview
32+ }
33+ active: webProcessMonitor.crashed || (webProcessMonitor.killed && !popupWebview.currentWebview.loading)
34+ sourceComponent: SadPage {
35+ webview: popupWebview
36+ objectName: "overlaySadPage"
37+ }
38+ WebProcessMonitor {
39+ id: webProcessMonitor
40+ webview: popupWebview
41+ }
42+ asynchronous: true
43+ }
44 }
45-
46 }
47
48=== added file 'src/app/webcontainer/SadPage.qml'
49--- src/app/webcontainer/SadPage.qml 1970-01-01 00:00:00 +0000
50+++ src/app/webcontainer/SadPage.qml 2015-09-29 20:15:01 +0000
51@@ -0,0 +1,56 @@
52+/*
53+ * Copyright 2015 Canonical Ltd.
54+ *
55+ * This file is part of webbrowser-app.
56+ *
57+ * webbrowser-app is free software; you can redistribute it and/or modify
58+ * it under the terms of the GNU General Public License as published by
59+ * the Free Software Foundation; version 3.
60+ *
61+ * webbrowser-app is distributed in the hope that it will be useful,
62+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
63+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64+ * GNU General Public License for more details.
65+ *
66+ * You should have received a copy of the GNU General Public License
67+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
68+ */
69+
70+import QtQuick 2.4
71+import Ubuntu.Components 1.3
72+
73+Rectangle {
74+ property var webview
75+
76+ Column {
77+ anchors {
78+ fill: parent
79+ margins: units.gu(4)
80+ }
81+ spacing: units.gu(4)
82+
83+ Image {
84+ anchors.horizontalCenter: parent.horizontalCenter
85+ source: "assets/tab-error.png"
86+ }
87+
88+ Label {
89+ anchors {
90+ left: parent.left
91+ right: parent.right
92+ }
93+
94+ wrapMode: Text.Wrap
95+ horizontalAlignment: Text.AlignHCenter
96+ text: webview ? i18n.tr("Oops, something went wrong.") : ""
97+ }
98+
99+ Button {
100+ anchors.horizontalCenter: parent.horizontalCenter
101+ objectName: "reloadButton"
102+ text: i18n.tr("Reload")
103+ color: UbuntuColors.green
104+ onClicked: webview.reload()
105+ }
106+ }
107+}
108
109=== modified file 'src/app/webcontainer/WebApp.qml'
110--- src/app/webcontainer/WebApp.qml 2015-08-18 14:18:37 +0000
111+++ src/app/webcontainer/WebApp.qml 2015-09-29 20:15:01 +0000
112@@ -29,21 +29,21 @@
113 id: webapp
114 objectName: "webappBrowserView"
115
116- currentWebview: webview.currentWebview
117+ currentWebview: containerWebView.currentWebview
118
119- property alias url: webview.url
120+ property alias url: containerWebView.url
121
122 property bool accountSwitcher
123
124 property string webappModelSearchPath: ""
125
126 property var webappUrlPatterns
127- property alias popupRedirectionUrlPrefixPattern: webview.popupRedirectionUrlPrefixPattern
128- property alias webviewOverrideFile: webview.webviewOverrideFile
129- property alias blockOpenExternalUrls: webview.blockOpenExternalUrls
130- property alias localUserAgentOverride: webview.localUserAgentOverride
131- property alias dataPath: webview.dataPath
132- property alias runningLocalApplication: webview.runningLocalApplication
133+ property alias popupRedirectionUrlPrefixPattern: containerWebView.popupRedirectionUrlPrefixPattern
134+ property alias webviewOverrideFile: containerWebView.webviewOverrideFile
135+ property alias blockOpenExternalUrls: containerWebView.blockOpenExternalUrls
136+ property alias localUserAgentOverride: containerWebView.localUserAgentOverride
137+ property alias dataPath: containerWebView.dataPath
138+ property alias runningLocalApplication: containerWebView.runningLocalApplication
139
140 property string webappName: ""
141
142@@ -60,15 +60,15 @@
143
144 actions: [
145 Actions.Back {
146- enabled: webapp.backForwardButtonsVisible && webview.currentWebview && webview.currentWebview.canGoBack
147- onTriggered: webview.currentWebview.goBack()
148+ enabled: webapp.backForwardButtonsVisible && containerWebView.currentWebview && containerWebView.currentWebview.canGoBack
149+ onTriggered: containerWebView.currentWebview.goBack()
150 },
151 Actions.Forward {
152- enabled: webapp.backForwardButtonsVisible && webview.currentWebview && webview.currentWebview.canGoForward
153- onTriggered: webview.currentWebview.goForward()
154+ enabled: webapp.backForwardButtonsVisible && containerWebView.currentWebview && containerWebView.currentWebview.canGoForward
155+ onTriggered: containerWebView.currentWebview.goForward()
156 },
157 Actions.Reload {
158- onTriggered: webview.currentWebview.reload()
159+ onTriggered: containerWebView.currentWebview.reload()
160 }
161 ]
162
163@@ -121,7 +121,7 @@
164 anchors.fill: parent
165
166 WebappContainerWebview {
167- id: webview
168+ id: containerWebView
169 objectName: "webview"
170
171 anchors {
172@@ -144,19 +144,39 @@
173 * being explictly defined here.
174 */
175 webappName: webapp.webappName === "" ? unityWebapps.name : webapp.webappName
176+
177+ Loader {
178+ anchors {
179+ fill: containerWebView
180+ topMargin: (!webapp.chromeless && chromeLoader.item.state == "shown")
181+ ? chromeLoader.item.height
182+ : 0
183+ }
184+ active: containerWebView.currentWebview &&
185+ (webProcessMonitor.crashed || (webProcessMonitor.killed && !containerWebView.currentWebview.loading))
186+ sourceComponent: SadPage {
187+ webview: containerWebView.currentWebview
188+ objectName: "mainWebviewSadPage"
189+ }
190+ WebProcessMonitor {
191+ id: webProcessMonitor
192+ webview: containerWebView.currentWebview
193+ }
194+ asynchronous: true
195+ }
196 }
197
198 Loader {
199 anchors {
200- fill: webview
201+ fill: containerWebView
202 topMargin: (!webapp.chromeless && chromeLoader.item.state == "shown") ? chromeLoader.item.height : 0
203 }
204 sourceComponent: ErrorSheet {
205- visible: webview.currentWebview && webview.currentWebview.lastLoadFailed
206- url: webview.currentWebview ? webview.currentWebview.url : ""
207+ visible: containerWebView.currentWebview && containerWebView.currentWebview.lastLoadFailed
208+ url: containerWebView.currentWebview ? containerWebView.currentWebview.url : ""
209 onRefreshClicked: {
210- if (webview.currentWebview)
211- webview.currentWebview.reload()
212+ if (containerWebView.currentWebview)
213+ containerWebView.currentWebview.reload()
214 }
215 }
216 asynchronous: true
217@@ -186,7 +206,7 @@
218 right: parent.right
219 }
220 height: units.gu(6)
221- y: webapp.currentWebview ? webview.currentWebview.locationBarController.offset : 0
222+ y: webapp.currentWebview ? containerWebView.currentWebview.locationBarController.offset : 0
223
224 onChooseAccount: webapp.chooseAccount()
225 }
226@@ -225,7 +245,7 @@
227 UnityWebApps.UnityWebApps {
228 id: unityWebapps
229 name: webappName
230- bindee: webview.currentWebview
231+ bindee: containerWebView.currentWebview
232 actionsContext: actionManager.globalContext
233 model: UnityWebApps.UnityWebappsAppModel { searchPath: webappModelSearchPath }
234 injectExtraUbuntuApis: runningLocalApplication
235
236=== added directory 'src/app/webcontainer/assets'
237=== added file 'src/app/webcontainer/assets/tab-error@27.png'
238Binary files src/app/webcontainer/assets/tab-error@27.png 1970-01-01 00:00:00 +0000 and src/app/webcontainer/assets/tab-error@27.png 2015-09-29 20:15:01 +0000 differ
239=== modified file 'tests/autopilot/webapp_container/tests/__init__.py'
240--- tests/autopilot/webapp_container/tests/__init__.py 2015-07-28 19:29:58 +0000
241+++ tests/autopilot/webapp_container/tests/__init__.py 2015-09-29 20:15:01 +0000
242@@ -16,7 +16,9 @@
243 """ Autopilot tests for the webapp_container package """
244
245 import os
246+import signal
247 import subprocess
248+import psutil
249
250 import fixtures
251 from autopilot.testcase import AutopilotTestCase
252@@ -120,6 +122,15 @@
253 webview.url = url
254 self.assert_page_eventually_loaded(url)
255
256+ def kill_web_processes(self, signal=signal.SIGKILL):
257+ children = psutil.Process(self.app.pid).children(True)
258+ for child in children:
259+ if child.name() == 'oxide-renderer':
260+ for arg in child.cmdline():
261+ if '--type=renderer' in arg:
262+ os.kill(child.pid, signal)
263+ break
264+
265
266 class WebappContainerTestCaseWithLocalContentBase(WebappContainerTestCaseBase):
267 BASE_URL_SCHEME = 'http://'
268
269=== added file 'tests/autopilot/webapp_container/tests/test_sad_tab.py'
270--- tests/autopilot/webapp_container/tests/test_sad_tab.py 1970-01-01 00:00:00 +0000
271+++ tests/autopilot/webapp_container/tests/test_sad_tab.py 2015-09-29 20:15:01 +0000
272@@ -0,0 +1,162 @@
273+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
274+# Copyright 2015 Canonical
275+#
276+# This program is free software: you can redistribute it and/or modify it
277+# under the terms of the GNU General Public License version 3, as published
278+# by the Free Software Foundation.
279+#
280+# This program is distributed in the hope that it will be useful,
281+# but WITHOUT ANY WARRANTY; without even the implied warranty of
282+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
283+# GNU General Public License for more details.
284+#
285+# You should have received a copy of the GNU General Public License
286+# along with this program. If not, see <http://www.gnu.org/licenses/>.
287+
288+import signal
289+import time
290+
291+from webapp_container.tests import WebappContainerTestCaseWithLocalContentBase
292+
293+from testtools.matchers import Equals, Contains, GreaterThan
294+from autopilot.matchers import Eventually
295+
296+import ubuntuuitoolkit as uitk
297+
298+
299+class SadPage(uitk.UbuntuUIToolkitCustomProxyObjectBase):
300+ def click_reload_button(self):
301+ button = self.select_single("Button", objectName="reloadButton")
302+ self.pointing_device.click_object(button)
303+
304+
305+class TestSadTab(WebappContainerTestCaseWithLocalContentBase):
306+ def _kill_web_process(self):
307+ self.kill_web_processes()
308+ time.sleep(1)
309+ self.assert_page_eventually_loaded(self.url)
310+
311+ self.kill_web_processes()
312+
313+ def _click_href_target_blank(self):
314+ webview = self.get_oxide_webview()
315+ self.assertThat(webview.url, Contains('/open-close-content'))
316+ gr = webview.globalRect
317+ self.pointing_device.move(
318+ gr.x + gr.width/4,
319+ gr.y + gr.height/4)
320+ self.pointing_device.click()
321+
322+ def _click_overlay(self):
323+ popup_controller = self.get_popup_controller()
324+ new_view_watcher = popup_controller.watch_signal(
325+ 'newViewCreated(QString)')
326+ animation_watcher = popup_controller.watch_signal(
327+ 'windowOverlayOpenAnimationDone()')
328+ animation_signal_emission = animation_watcher.num_emissions
329+
330+ views = self.get_popup_overlay_views()
331+ self.assertThat(len(views), Equals(0))
332+
333+ self._click_href_target_blank()
334+
335+ self.assertThat(
336+ lambda: new_view_watcher.was_emitted,
337+ Eventually(Equals(True)))
338+ self.assertThat(
339+ lambda: len(self.get_popup_overlay_views()),
340+ Eventually(Equals(1)))
341+ views = self.get_popup_overlay_views()
342+ overlay = views[0]
343+ self.assertThat(
344+ overlay.select_single(objectName="overlayWebview").url,
345+ Contains('/open-close-content'))
346+
347+ self.assertThat(
348+ lambda: animation_watcher.num_emissions,
349+ Eventually(GreaterThan(animation_signal_emission)))
350+
351+ def test_reload_main_webview_killed(self):
352+ self.launch_webcontainer_app_with_local_http_server([])
353+ self.get_webcontainer_window().visible.wait_for(True)
354+
355+ self._kill_web_process()
356+
357+ sad_webview = self.app.wait_select_single(
358+ SadPage,
359+ objectName="mainWebviewSadPage")
360+ sad_webview.click_reload_button()
361+ sad_webview.wait_until_destroyed()
362+
363+ self.assert_page_eventually_loaded(self.url)
364+
365+ def test_reload_overlay_killed(self):
366+ args = []
367+ self.launch_webcontainer_app_with_local_http_server(
368+ args,
369+ '/open-close-content')
370+ self.get_webcontainer_window().visible.wait_for(True)
371+
372+ self._click_overlay()
373+
374+ self._kill_web_process()
375+
376+ sad_webview = self.app.wait_select_single(
377+ SadPage,
378+ objectName="overlaySadPage")
379+
380+ sad_webview.click_reload_button()
381+ sad_webview.wait_until_destroyed()
382+
383+ views = self.get_popup_overlay_views()
384+ overlay = views[0]
385+ self.assertThat(
386+ lambda: overlay.wait_select_single(
387+ objectName="overlayWebview").url,
388+ Eventually(Contains('/open-close-content')))
389+
390+ def _crash_web_process(self):
391+ self.kill_web_processes(signal.SIGABRT)
392+
393+ def test_reload_main_webview_crashed(self):
394+ self.launch_webcontainer_app_with_local_http_server([])
395+ self.get_webcontainer_window().visible.wait_for(True)
396+
397+ self._crash_web_process()
398+
399+ sad_webview = self.app.wait_select_single(
400+ SadPage,
401+ objectName="mainWebviewSadPage")
402+
403+ sad_webview.click_reload_button()
404+ sad_webview.wait_until_destroyed()
405+
406+ self.assert_page_eventually_loaded(self.url)
407+
408+ def test_reload_overlay_crashed(self):
409+ args = []
410+ self.launch_webcontainer_app_with_local_http_server(
411+ args,
412+ '/open-close-content')
413+ self.get_webcontainer_window().visible.wait_for(True)
414+
415+ self._click_overlay()
416+ self.assertThat(
417+ lambda: len(self.get_popup_overlay_views()),
418+ Eventually(Equals(1)))
419+
420+ self._crash_web_process()
421+
422+ sad_webview = self.app.wait_select_single(
423+ SadPage,
424+ objectName="overlaySadPage")
425+
426+ sad_webview.click_reload_button()
427+ sad_webview.wait_until_destroyed()
428+
429+ views = self.get_popup_overlay_views()
430+ overlay = views[0]
431+ self.assertThat(
432+ lambda: overlay.wait_select_single(
433+ objectName="overlayWebview").url,
434+ Eventually(Contains('/open-close-content')))

Subscribers

People subscribed via source and target branches

to status/vote changes: