Merge lp:~osomon/webbrowser-app/save-restore-state into lp:webbrowser-app
- save-restore-state
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Olivier Tilloy | ||||
Approved revision: | 834 | ||||
Merged at revision: | 886 | ||||
Proposed branch: | lp:~osomon/webbrowser-app/save-restore-state | ||||
Merge into: | lp:webbrowser-app | ||||
Diff against target: |
242 lines (+45/-70) 5 files modified
debian/control (+2/-2) src/app/webbrowser/Browser.qml (+27/-16) src/app/webbrowser/BrowserTab.qml (+13/-1) tests/autopilot/webbrowser_app/tests/test_session_save_restore.py (+0/-51) tests/unittests/qml/tst_BrowserTab.qml (+3/-0) |
||||
To merge this branch: | bzr merge lp:~osomon/webbrowser-app/save-restore-state | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt (community) | Approve | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+242678@code.launchpad.net |
Commit message
Use the new restoreState API in oxide 1.4 to properly save and restore navigation state across sessions.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
- 826. By Olivier Tilloy
-
Merge the latest changes from trunk,
and resolve a conflict.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:826
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 827. By Olivier Tilloy
-
Merge the latest changes from trunk,
and resolve a conflict. - 828. By Olivier Tilloy
-
Remove a not very useful comment.
- 829. By Olivier Tilloy
-
Rename the 'blob' property to 'savedState', which is much more explicit.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:829
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 830. By Olivier Tilloy
-
Merge the latest changes from trunk,
and resolve a couple of conflicts. - 831. By Olivier Tilloy
-
Fix BrowserTab unit tests.
- 832. By Olivier Tilloy
-
Revert accidental change introduced when merging latest trunk.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:832
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 833. By Olivier Tilloy
-
Merge the latest changes from trunk, and resolve a conflict.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:833
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:833
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 834. By Olivier Tilloy
-
Bump the required oxide version in the build dependencies as well, since unit tests require oxide 1.4.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:834
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:834
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'debian/control' | |||
2 | --- debian/control 2015-01-19 10:45:25 +0000 | |||
3 | +++ debian/control 2015-01-28 14:25:23 +0000 | |||
4 | @@ -6,7 +6,7 @@ | |||
5 | 6 | debhelper (>= 9), | 6 | debhelper (>= 9), |
6 | 7 | dh-translations, | 7 | dh-translations, |
7 | 8 | hardening-wrapper, | 8 | hardening-wrapper, |
9 | 9 | liboxideqt-qmlplugin (>= 1.3), | 9 | liboxideqt-qmlplugin (>= 1.4), |
10 | 10 | libqt5sql5-sqlite, | 10 | libqt5sql5-sqlite, |
11 | 11 | python-flake8, | 11 | python-flake8, |
12 | 12 | python3-all, | 12 | python3-all, |
13 | @@ -34,7 +34,7 @@ | |||
14 | 34 | Depends: ${misc:Depends}, | 34 | Depends: ${misc:Depends}, |
15 | 35 | ${shlibs:Depends}, | 35 | ${shlibs:Depends}, |
16 | 36 | fonts-liberation, | 36 | fonts-liberation, |
18 | 37 | liboxideqt-qmlplugin (>= 1.1), | 37 | liboxideqt-qmlplugin (>= 1.4), |
19 | 38 | libqt5sql5-sqlite, | 38 | libqt5sql5-sqlite, |
20 | 39 | qml-module-qtquick2 | qtdeclarative5-qtquick2-plugin, | 39 | qml-module-qtquick2 | qtdeclarative5-qtquick2-plugin, |
21 | 40 | qml-module-qtquick-dialogs | qtdeclarative5-dialogs-plugin, | 40 | qml-module-qtquick-dialogs | qtdeclarative5-dialogs-plugin, |
22 | 41 | 41 | ||
23 | === modified file 'src/app/webbrowser/Browser.qml' | |||
24 | --- src/app/webbrowser/Browser.qml 2014-12-17 09:22:40 +0000 | |||
25 | +++ src/app/webbrowser/Browser.qml 2015-01-28 14:25:23 +0000 | |||
26 | @@ -17,7 +17,7 @@ | |||
27 | 17 | */ | 17 | */ |
28 | 18 | 18 | ||
29 | 19 | import QtQuick 2.0 | 19 | import QtQuick 2.0 |
31 | 20 | import com.canonical.Oxide 1.0 as Oxide | 20 | import com.canonical.Oxide 1.4 as Oxide |
32 | 21 | import Ubuntu.Components 1.1 | 21 | import Ubuntu.Components 1.1 |
33 | 22 | import webbrowserapp.private 0.1 | 22 | import webbrowserapp.private 0.1 |
34 | 23 | import webbrowsercommon.private 0.1 | 23 | import webbrowsercommon.private 0.1 |
35 | @@ -415,7 +415,18 @@ | |||
36 | 415 | id: newTabViewLoader | 415 | id: newTabViewLoader |
37 | 416 | anchors.fill: parent | 416 | anchors.fill: parent |
38 | 417 | 417 | ||
40 | 418 | sourceComponent: !parent.url.toString() ? newTabViewComponent : undefined | 418 | // Avoid loading the new tab view if the webview is about to load |
41 | 419 | // content. Since WebView.restoreState is not a notifyable property, | ||
42 | 420 | // this can’t be achieved with a simple property binding. | ||
43 | 421 | Component.onCompleted: { | ||
44 | 422 | if (!parent.url.toString() && !parent.restoreState) { | ||
45 | 423 | sourceComponent = newTabViewComponent | ||
46 | 424 | } | ||
47 | 425 | } | ||
48 | 426 | Connections { | ||
49 | 427 | target: newTabViewLoader.parent | ||
50 | 428 | onUrlChanged: newTabViewLoader.sourceComponent = null | ||
51 | 429 | } | ||
52 | 419 | 430 | ||
53 | 420 | Component { | 431 | Component { |
54 | 421 | id: newTabViewComponent | 432 | id: newTabViewComponent |
55 | @@ -542,39 +553,39 @@ | |||
56 | 542 | } | 553 | } |
57 | 543 | 554 | ||
58 | 544 | // Those two functions are used to save/restore the current state of a tab. | 555 | // Those two functions are used to save/restore the current state of a tab. |
59 | 545 | // The current implementation is naive, it only saves/restores the current URL. | ||
60 | 546 | // In the future, we’ll want to rely on oxide to save and restore a full state | ||
61 | 547 | // of the corresponding webview as a binary blob, which includes navigation | ||
62 | 548 | // history, current scroll offset and form data. See http://pad.lv/1353143. | ||
63 | 549 | function serializeTabState(tab) { | 556 | function serializeTabState(tab) { |
64 | 550 | var state = {} | 557 | var state = {} |
65 | 551 | state.uniqueId = tab.uniqueId | 558 | state.uniqueId = tab.uniqueId |
66 | 552 | state.url = tab.url.toString() | 559 | state.url = tab.url.toString() |
67 | 553 | state.title = tab.title | 560 | state.title = tab.title |
68 | 554 | state.preview = tab.preview.toString() | 561 | state.preview = tab.preview.toString() |
69 | 562 | state.savedState = tab.webview ? tab.webview.currentState : tab.restoreState | ||
70 | 555 | return state | 563 | return state |
71 | 556 | } | 564 | } |
72 | 557 | 565 | ||
73 | 558 | function createTabFromState(state) { | 566 | function createTabFromState(state) { |
75 | 559 | var properties = {"initialUrl": state.url, "initialTitle": state.title} | 567 | var properties = {'initialUrl': state.url, 'initialTitle': state.title} |
76 | 560 | if ('uniqueId' in state) { | 568 | if ('uniqueId' in state) { |
77 | 561 | properties["uniqueId"] = state.uniqueId | 569 | properties["uniqueId"] = state.uniqueId |
78 | 562 | } | 570 | } |
79 | 563 | if ('preview' in state) { | 571 | if ('preview' in state) { |
80 | 564 | properties["preview"] = state.preview | 572 | properties["preview"] = state.preview |
81 | 565 | } | 573 | } |
82 | 574 | if ('savedState' in state) { | ||
83 | 575 | properties['restoreState'] = state.savedState | ||
84 | 576 | properties['restoreType'] = Oxide.WebView.RestoreLastSessionExitedCleanly | ||
85 | 577 | } | ||
86 | 566 | return tabComponent.createObject(tabContainer, properties) | 578 | return tabComponent.createObject(tabContainer, properties) |
87 | 567 | } | 579 | } |
88 | 568 | } | 580 | } |
89 | 569 | Connections { | 581 | Connections { |
98 | 570 | target: tabsModel | 582 | target: Qt.application |
99 | 571 | onCurrentTabChanged: session.save() | 583 | onStateChanged: { |
100 | 572 | onCountChanged: session.save() | 584 | if (Qt.application.state != Qt.ApplicationActive) { |
101 | 573 | } | 585 | session.save() |
102 | 574 | Connections { | 586 | } |
103 | 575 | target: browser.currentWebview | 587 | } |
104 | 576 | onUrlChanged: session.save() | 588 | onAboutToQuit: session.save() |
97 | 577 | onTitleChanged: session.save() | ||
105 | 578 | } | 589 | } |
106 | 579 | 590 | ||
107 | 580 | // Delay instantiation of the first webview by 1 msec to allow initial | 591 | // Delay instantiation of the first webview by 1 msec to allow initial |
108 | @@ -596,7 +607,7 @@ | |||
109 | 596 | browser.openUrlInNewTab(browser.homepage, true, false) | 607 | browser.openUrlInNewTab(browser.homepage, true, false) |
110 | 597 | } | 608 | } |
111 | 598 | tabsModel.currentTab.load() | 609 | tabsModel.currentTab.load() |
113 | 599 | if (!tabsModel.currentTab.url.toString() && (formFactor == "desktop")) { | 610 | if (!tabsModel.currentTab.url.toString() && !tabsModel.currentTab.restoreState && (formFactor == "desktop")) { |
114 | 600 | internal.focusAddressBar() | 611 | internal.focusAddressBar() |
115 | 601 | } | 612 | } |
116 | 602 | } | 613 | } |
117 | 603 | 614 | ||
118 | === modified file 'src/app/webbrowser/BrowserTab.qml' | |||
119 | --- src/app/webbrowser/BrowserTab.qml 2014-12-16 16:25:58 +0000 | |||
120 | +++ src/app/webbrowser/BrowserTab.qml 2015-01-28 14:25:23 +0000 | |||
121 | @@ -17,12 +17,15 @@ | |||
122 | 17 | */ | 17 | */ |
123 | 18 | 18 | ||
124 | 19 | import QtQuick 2.0 | 19 | import QtQuick 2.0 |
125 | 20 | import com.canonical.Oxide 1.4 as Oxide | ||
126 | 20 | import webbrowserapp.private 0.1 | 21 | import webbrowserapp.private 0.1 |
127 | 21 | 22 | ||
128 | 22 | FocusScope { | 23 | FocusScope { |
129 | 23 | property string uniqueId: this.toString() + "-" + Date.now() | 24 | property string uniqueId: this.toString() + "-" + Date.now() |
130 | 24 | property url initialUrl | 25 | property url initialUrl |
131 | 25 | property string initialTitle | 26 | property string initialTitle |
132 | 27 | property string restoreState | ||
133 | 28 | property int restoreType | ||
134 | 26 | property var request | 29 | property var request |
135 | 27 | property Component webviewComponent | 30 | property Component webviewComponent |
136 | 28 | readonly property var webview: (children.length == 1) ? children[0] : null | 31 | readonly property var webview: (children.length == 1) ? children[0] : null |
137 | @@ -33,7 +36,14 @@ | |||
138 | 33 | 36 | ||
139 | 34 | function load() { | 37 | function load() { |
140 | 35 | if (!webview) { | 38 | if (!webview) { |
142 | 36 | webviewComponent.incubateObject(this, {"url": initialUrl}) | 39 | var properties = {} |
143 | 40 | if (restoreState) { | ||
144 | 41 | properties['restoreState'] = restoreState | ||
145 | 42 | properties['restoreType'] = restoreType | ||
146 | 43 | } else { | ||
147 | 44 | properties['url'] = initialUrl | ||
148 | 45 | } | ||
149 | 46 | webviewComponent.incubateObject(this, properties) | ||
150 | 37 | } | 47 | } |
151 | 38 | } | 48 | } |
152 | 39 | 49 | ||
153 | @@ -41,6 +51,8 @@ | |||
154 | 41 | if (webview) { | 51 | if (webview) { |
155 | 42 | initialUrl = webview.url | 52 | initialUrl = webview.url |
156 | 43 | initialTitle = webview.title | 53 | initialTitle = webview.title |
157 | 54 | restoreState = webview.currentState | ||
158 | 55 | restoreType = Oxide.WebView.RestoreCurrentSession | ||
159 | 44 | webview.destroy() | 56 | webview.destroy() |
160 | 45 | } | 57 | } |
161 | 46 | } | 58 | } |
162 | 47 | 59 | ||
163 | === removed file 'tests/autopilot/webbrowser_app/tests/test_session_save_restore.py' | |||
164 | --- tests/autopilot/webbrowser_app/tests/test_session_save_restore.py 2014-12-18 18:39:47 +0000 | |||
165 | +++ tests/autopilot/webbrowser_app/tests/test_session_save_restore.py 1970-01-01 00:00:00 +0000 | |||
166 | @@ -1,51 +0,0 @@ | |||
167 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
168 | 2 | # | ||
169 | 3 | # Copyright 2014 Canonical | ||
170 | 4 | # | ||
171 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
172 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
173 | 7 | # by the Free Software Foundation. | ||
174 | 8 | # | ||
175 | 9 | # This program is distributed in the hope that it will be useful, | ||
176 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
177 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
178 | 12 | # GNU General Public License for more details. | ||
179 | 13 | # | ||
180 | 14 | # You should have received a copy of the GNU General Public License | ||
181 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
182 | 16 | |||
183 | 17 | from autopilot.matchers import Eventually | ||
184 | 18 | from testtools.matchers import Equals | ||
185 | 19 | |||
186 | 20 | from webbrowser_app.tests import StartOpenRemotePageTestCaseBase | ||
187 | 21 | |||
188 | 22 | |||
189 | 23 | class TestSessionSaveRestore(StartOpenRemotePageTestCaseBase): | ||
190 | 24 | |||
191 | 25 | def create_new_tab(self, url): | ||
192 | 26 | self.open_tabs_view() | ||
193 | 27 | self.open_new_tab() | ||
194 | 28 | new_tab_view = self.main_window.get_new_tab_view() | ||
195 | 29 | self.main_window.go_to_url(url) | ||
196 | 30 | new_tab_view.wait_until_destroyed() | ||
197 | 31 | |||
198 | 32 | def test_session_is_saved_and_restored(self): | ||
199 | 33 | paths = ["/test1", "/test2", "/wait/0"] | ||
200 | 34 | for path in paths[1:]: | ||
201 | 35 | self.create_new_tab(self.base_url + path) | ||
202 | 36 | self.open_tabs_view() | ||
203 | 37 | tabs_view = self.main_window.get_tabs_view() | ||
204 | 38 | self.assertThat(tabs_view.count, Eventually(Equals(len(paths)))) | ||
205 | 39 | process = self.app.process | ||
206 | 40 | process.kill() | ||
207 | 41 | process.wait(10) | ||
208 | 42 | self.ARGS = [] | ||
209 | 43 | self.app = self.launch_app() | ||
210 | 44 | self.open_tabs_view() | ||
211 | 45 | tabs_view = self.main_window.get_tabs_view() | ||
212 | 46 | self.assertThat(tabs_view.count, Eventually(Equals(len(paths)))) | ||
213 | 47 | previews = tabs_view.get_ordered_previews() | ||
214 | 48 | for i in range(len(paths)): | ||
215 | 49 | self.assertThat(previews[len(paths) - 1 - i].url, | ||
216 | 50 | Eventually(Equals(self.base_url + paths[i]))) | ||
217 | 51 | self.assert_number_webviews_eventually(1) | ||
218 | 52 | 0 | ||
219 | === modified file 'tests/unittests/qml/tst_BrowserTab.qml' | |||
220 | --- tests/unittests/qml/tst_BrowserTab.qml 2014-12-16 17:24:32 +0000 | |||
221 | +++ tests/unittests/qml/tst_BrowserTab.qml 2015-01-28 14:25:23 +0000 | |||
222 | @@ -35,6 +35,7 @@ | |||
223 | 35 | property string title | 35 | property string title |
224 | 36 | property url icon | 36 | property url icon |
225 | 37 | property var request | 37 | property var request |
226 | 38 | property string currentState | ||
227 | 38 | } | 39 | } |
228 | 39 | readonly property bool webviewPresent: webview | 40 | readonly property bool webviewPresent: webview |
229 | 40 | readonly property bool captureTakerPresent: captureTaker != undefined | 41 | readonly property bool captureTakerPresent: captureTaker != undefined |
230 | @@ -66,10 +67,12 @@ | |||
231 | 66 | 67 | ||
232 | 67 | tab.webview.url = "http://ubuntu.com" | 68 | tab.webview.url = "http://ubuntu.com" |
233 | 68 | tab.webview.title = "Ubuntu" | 69 | tab.webview.title = "Ubuntu" |
234 | 70 | tab.webview.currentState = "foobar" | ||
235 | 69 | tab.unload() | 71 | tab.unload() |
236 | 70 | tryCompare(tab, 'webviewPresent', false) | 72 | tryCompare(tab, 'webviewPresent', false) |
237 | 71 | compare(tab.initialUrl, "http://ubuntu.com") | 73 | compare(tab.initialUrl, "http://ubuntu.com") |
238 | 72 | compare(tab.initialTitle, "Ubuntu") | 74 | compare(tab.initialTitle, "Ubuntu") |
239 | 75 | compare(tab.restoreState, "foobar") | ||
240 | 73 | 76 | ||
241 | 74 | tab.destroy() | 77 | tab.destroy() |
242 | 75 | } | 78 | } |
FAILED: Continuous integration, rev:825 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 1304/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 334/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- vivid/220/ console jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 62/console jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 62 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 62/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 62 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-mako/ 305/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 334 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 334/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 16179 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-amd64/ 224/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 1304/rebuild
http://