Merge lp:~abreu-alexandre/webbrowser-app/window-open-overlay into lp:webbrowser-app
- window-open-overlay
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Olivier Tilloy |
Approved revision: | 989 |
Merged at revision: | 989 |
Proposed branch: | lp:~abreu-alexandre/webbrowser-app/window-open-overlay |
Merge into: | lp:webbrowser-app |
Diff against target: |
1006 lines (+612/-253) 9 files modified
src/app/webcontainer/PopupWindowController.qml (+181/-0) src/app/webcontainer/PopupWindowOverlay.qml (+199/-0) src/app/webcontainer/WebViewImplOxide.qml (+13/-130) src/app/webcontainer/WebappContainerWebview.qml (+11/-1) tests/autopilot/webapp_container/tests/__init__.py (+6/-0) tests/autopilot/webapp_container/tests/fake_servers.py (+32/-11) tests/autopilot/webapp_container/tests/test_popup_webview_overlay.py (+170/-0) tests/autopilot/webapp_container/tests/test_redirection_pattern.py (+0/-59) tests/autopilot/webapp_container/tests/test_url_patterns.py (+0/-52) |
To merge this branch: | bzr merge lp:~abreu-alexandre/webbrowser-app/window-open-overlay |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Olivier Tilloy | Approve | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+248000@code.launchpad.net |
Commit message
Add multi-window support for webapps
Description of the change
Add multi-window support for webapps
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:944
http://
Executed test runs:
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:945
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:946
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
Not sure exactly what the intended UX behaviour is (the spec is a bit summary), this is what I’m seeing when testing on the desktop:
- The animation to open an overlay is quite fast, almost imperceptible (could we maybe make it a tad slower?)
- When dismissing an overlay (by dragging it downwards or clicking on the chevron), it’s destroyed, right? I’m thinking that in that case maybe a cross icon would be more appropriate than a chevron, but of course that’s for a visual designer to decide.
- When opening an overlay, the current webview seems to be hidden instantaneously, it should be hidden only once the animation has completed.
- Similarly, when dragging an overlay downwards, nothing is visible behind, I think the main webview (or the previous overlay if any) should be visible.
- After opening 3 overlays, the fourth external link gets open in my desktop browser (chromium) as expected, but then after closing one overlay and starting to re-open more external links, I got in a situation where blank chromium windows would open instead of a new tab in the current instance, this is weird (tested with initially opening the container on http://
qml: Maximum number of popup overlay opened, opening: about:blank in the browser
Olivier Tilloy (osomon) wrote : | # |
Also, I’m thinking that this behaviour makes sense on devices, but does it on desktop, where it’s much more visible when a new tab opens in a browser?
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:948
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 948. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
Alexandre Abreu (abreu-alexandre) wrote : | # |
> Not sure exactly what the intended UX behaviour is (the spec is a bit
> summary), this is what I’m seeing when testing on the desktop:
>
> - The animation to open an overlay is quite fast, almost imperceptible (could
> we maybe make it a tad slower?)
agree, done
> - When dismissing an overlay (by dragging it downwards or clicking on the
> chevron), it’s destroyed, right? I’m thinking that in that case maybe a cross
> icon would be more appropriate than a chevron, but of course that’s for a
> visual designer to decide.
yeah, those are the designs I had from "design", ... imo it is rather understandable,
> - When opening an overlay, the current webview seems to be hidden
> instantaneously, it should be hidden only once the animation has completed.
>
> - Similarly, when dragging an overlay downwards, nothing is visible behind, I
> think the main webview (or the previous overlay if any) should be visible.
I updated the behavior to be cleaner & slicker,
> - After opening 3 overlays, the fourth external link gets open in my desktop
> browser (chromium) as expected, but then after closing one overlay and
> starting to re-open more external links, I got in a situation where blank
> chromium windows would open instead of a new tab in the current instance, this
> is weird (tested with initially opening the container on
> http://
> on the console:
>
> qml: Maximum number of popup overlay opened, opening: about:blank in the
> browser
mmh I was not able to repro that ...
Alexandre Abreu (abreu-alexandre) wrote : | # |
> Also, I’m thinking that this behaviour makes sense on devices, but does it on
> desktop, where it’s much more visible when a new tab opens in a browser?
I would have to check with design, but I still does make sense on desktop too.
The behavior might be adjusted a bit but it does not feel too awkward.
We dont support tabs for webapps no matter what the environment is, and when thinking
about tabs+webapps on desktop it is not obvious to me how to have it work in a clean way
for the user. Adding tabs is somewhat out of the question (at least atm) I think,
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:949
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 949. By Olivier Tilloy
-
Use the new locationBarCont
roller API available in oxide 1.5 to control the position of the chrome. Fixes: #1365179, #1429132
Approved by: Alexandre Abreu - 950. By Olivier Tilloy
-
Rewrite URLs with an uppercase scheme. Fixes: #1436312
- 951. By Olivier Tilloy
-
Remove two broken symlinks.
- 952. By Olivier Tilloy
-
Always initialize member attribute at construction time.
Not really an issue here, but flagged by coverity (https:/
/scan.coverity. com/projects/ 4565), and easy enough to address. - 953. By CI Train Bot Account
-
Releasing 0.23+15.
04.20150331. 2-0ubuntu1 - 954. By CI Train Bot Account
-
Resync trunk.
- 955. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 956. By Riccardo Padovani
-
Add settings page, per design specification. This adds qml-module-
qt-labs- folderlistmodel and qml-module- qt-labs- settings as runtime dependencies for webbrowser-app. Fixes: #1351183
Approved by: Bill Filler, Olivier Tilloy - 957. By Olivier Tilloy
-
Autopilot tests for the settings UI.
- 958. By Olivier Tilloy
-
Add a "Clear Cache" entry under the privacy settings. Fixes: #1260014, #1296364
Approved by: PS Jenkins bot - 959. By CI Train Bot Account
-
Releasing 0.23+15.
04.20150408- 0ubuntu1 - 960. By Olivier Tilloy
-
Update translation template.
- 961. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 962. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 963. By Alexandre Abreu
-
remove qtwebkit deps (LP: #1362640) Fixes: #1362640
Approved by: Timo Jyrinki, PS Jenkins bot, Olivier Tilloy - 964. By Justin McPherson <justin@phablet-dev>
-
Command line options for media-hub use through Oxide.
Approved by: PS Jenkins bot - 965. By CI Train Bot Account
-
Releasing 0.23+15.
04.20150410- 0ubuntu1 - 966. By CI Train Bot Account
-
Resync trunk.
- 967. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
Olivier Tilloy (osomon) wrote : | # |
There’s a couple of trivial conflicts when merging into the latest trunk.
Olivier Tilloy (osomon) wrote : | # |
I’m seeing 3 failures when running the autopilot tests on my desktop:
webapp_
webapp_
webapp_
Alexandre Abreu (abreu-alexandre) wrote : | # |
updated
- 968. By Arthur Mello
-
Add model support to control which history entries will be displayed based on a blacklist database
Approved by: PS Jenkins bot, Olivier Tilloy - 969. By Arthur Mello
-
Make Top Sites format equal to Bookmarks on the New Tab view
Approved by: PS Jenkins bot - 970. By CI Train Bot Account
-
Releasing 0.23+15.
04.20150416- 0ubuntu1
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:970
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 971. By CI Train Bot Account
-
Resync trunk.
- 972. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
Olivier Tilloy (osomon) wrote : | # |
I’m still seeing autopilot test failures when run on my desktop (up-to-date vivid): http://
- 973. By Ugo Riboni
-
Include bookmark results in the suggestions list Fixes: #1351177
Approved by: Olivier Tilloy, PS Jenkins bot - 974. By Alexandre Abreu
-
Add missing reload button from the webapp container as specified in the design document.
Approved by: Olivier Tilloy, PS Jenkins bot - 975. By Ken VanDine
-
added ShareLink to contextualActions
Approved by: Olivier Tilloy, PS Jenkins bot
- 976. By Olivier Tilloy
-
Save the updated homepage when pressing return. Fixes: #1441874
Approved by: PS Jenkins bot, Riccardo Padovani - 977. By Leo Arias
-
In the autopilot tests, removed the extra focus step to write a URL. Fixes: #1441551
Approved by: Olivier Tilloy, PS Jenkins bot - 978. By Olivier Tilloy
-
Always exit fullscreen mode when the application becomes inactive. Fixes: #1331475
Approved by: PS Jenkins bot, Bill Filler - 979. By Olivier Tilloy
-
Recognize about:blank as a valid URL. Fixes: #1444139
Approved by: PS Jenkins bot - 980. By CI Train Bot Account
-
Releasing 0.23+15.
04.20150422. 1-0ubuntu1 - 981. By CI Train Bot Account
-
Resync trunk.
- 982. By Olivier Tilloy
-
Update translation template.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:970
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 983. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 984. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 985. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 986. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 987. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 988. By Alexandre Abreu
-
Window overlay for popups
- 989. By Alexandre Abreu
-
Fix AP global coords used for the clicks
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:972
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:989
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
That looks good to me now, and the autopilot tests are all passing on my laptop.
- 990. By Alexandre Abreu
-
tweak to properly trigger elide
Preview Diff
1 | === added file 'src/app/webcontainer/PopupWindowController.qml' | |||
2 | --- src/app/webcontainer/PopupWindowController.qml 1970-01-01 00:00:00 +0000 | |||
3 | +++ src/app/webcontainer/PopupWindowController.qml 2015-04-30 16:33:24 +0000 | |||
4 | @@ -0,0 +1,181 @@ | |||
5 | 1 | /* | ||
6 | 2 | * Copyright 2014 Canonical Ltd. | ||
7 | 3 | * | ||
8 | 4 | * This file is part of webbrowser-app. | ||
9 | 5 | * | ||
10 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
11 | 7 | * it under the terms of the GNU General Public License as published by | ||
12 | 8 | * the Free Software Foundation; version 3. | ||
13 | 9 | * | ||
14 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
15 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | 13 | * GNU General Public License for more details. | ||
18 | 14 | * | ||
19 | 15 | * You should have received a copy of the GNU General Public License | ||
20 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
21 | 17 | */ | ||
22 | 18 | |||
23 | 19 | import QtQuick 2.0 | ||
24 | 20 | import com.canonical.Oxide 1.0 as Oxide | ||
25 | 21 | import Ubuntu.Components 1.1 | ||
26 | 22 | import Ubuntu.Components.Popups 1.0 | ||
27 | 23 | |||
28 | 24 | Item { | ||
29 | 25 | id: controller | ||
30 | 26 | |||
31 | 27 | property var webappUrlPatterns | ||
32 | 28 | property var mainWebappView | ||
33 | 29 | property var views: [] | ||
34 | 30 | property bool blockOpenExternalUrls: false | ||
35 | 31 | |||
36 | 32 | // Used to access runtime behavior during tests | ||
37 | 33 | signal openExternalUrlTriggered(string url) | ||
38 | 34 | signal newViewCreated(string url) | ||
39 | 35 | signal windowOverlayOpenAnimationDone() | ||
40 | 36 | |||
41 | 37 | readonly property int maxSimultaneousViews: 3 | ||
42 | 38 | |||
43 | 39 | function openUrlExternally(url) { | ||
44 | 40 | if (!blockOpenExternalUrls) { | ||
45 | 41 | Qt.openUrlExternally(url) | ||
46 | 42 | } | ||
47 | 43 | openExternalUrlTriggered(url) | ||
48 | 44 | } | ||
49 | 45 | |||
50 | 46 | function onOverlayMoved(popup, diffY) { | ||
51 | 47 | if ((popup.y + diffY) > 0) { | ||
52 | 48 | popup.y += diffY | ||
53 | 49 | } | ||
54 | 50 | } | ||
55 | 51 | function handleNewViewAdded(view) { | ||
56 | 52 | if (views.length !== 0) { | ||
57 | 53 | var topView = views[views.length-1] | ||
58 | 54 | } | ||
59 | 55 | views.push(view) | ||
60 | 56 | } | ||
61 | 57 | function handleOpenInUrlBrowserForView(url, view) { | ||
62 | 58 | handleViewRemoved(view) | ||
63 | 59 | openExternalUrlTriggered(url) | ||
64 | 60 | openUrlExternally(url) | ||
65 | 61 | } | ||
66 | 62 | function createViewSlidingHandlerFor(newView, viewBelow) { | ||
67 | 63 | var parentHeight = viewBelow.parent.height | ||
68 | 64 | return function() { | ||
69 | 65 | if (viewBelow && newView) { | ||
70 | 66 | viewBelow.opacity = | ||
71 | 67 | newView.y / parentHeight | ||
72 | 68 | } | ||
73 | 69 | } | ||
74 | 70 | } | ||
75 | 71 | function topViewOnStack() { | ||
76 | 72 | if (views.length !== 0) { | ||
77 | 73 | return views[views.length-1] | ||
78 | 74 | } | ||
79 | 75 | return mainWebappView | ||
80 | 76 | } | ||
81 | 77 | function handleViewRemoved(view) { | ||
82 | 78 | if (views.length === 0) { | ||
83 | 79 | return | ||
84 | 80 | } | ||
85 | 81 | |||
86 | 82 | var topMostView = views[views.length-1] | ||
87 | 83 | if (topMostView !== view) { | ||
88 | 84 | return | ||
89 | 85 | } | ||
90 | 86 | views.pop() | ||
91 | 87 | |||
92 | 88 | var parentHeight = topMostView.parent.height | ||
93 | 89 | var nextView = topViewOnStack() | ||
94 | 90 | nextView.visible = true | ||
95 | 91 | |||
96 | 92 | function onViewSlidingOut() { | ||
97 | 93 | if (topMostView.y >= (topMostView.parent.height - 10)) { | ||
98 | 94 | topMostView.yChanged.disconnect(onViewSlidingOut) | ||
99 | 95 | topMostView.destroy() | ||
100 | 96 | |||
101 | 97 | updateViewVisibility(nextView, true) | ||
102 | 98 | } | ||
103 | 99 | } | ||
104 | 100 | topMostView.yChanged.connect(onViewSlidingOut) | ||
105 | 101 | topMostView.y = topMostView.parent.height | ||
106 | 102 | } | ||
107 | 103 | function createPopupView(parentView, request, isRequestFromMainWebappWebview, context) { | ||
108 | 104 | var view = popupWebOverlayFactory.createObject( | ||
109 | 105 | parentView, | ||
110 | 106 | { request: request, | ||
111 | 107 | webContext: context, | ||
112 | 108 | popupWindowController: controller }); | ||
113 | 109 | |||
114 | 110 | var topMostView = topViewOnStack() | ||
115 | 111 | |||
116 | 112 | // handle opacity updates of the view below this one | ||
117 | 113 | // when the view is sliding | ||
118 | 114 | view.yChanged.connect( | ||
119 | 115 | createViewSlidingHandlerFor(view, topMostView)) | ||
120 | 116 | |||
121 | 117 | function onViewSlidingIn() { | ||
122 | 118 | var parentHeight = view.parent.height | ||
123 | 119 | |||
124 | 120 | if (view.y <= 10) { | ||
125 | 121 | view.yChanged.disconnect(onViewSlidingIn) | ||
126 | 122 | |||
127 | 123 | updateViewVisibility(topMostView, false) | ||
128 | 124 | } | ||
129 | 125 | } | ||
130 | 126 | view.yChanged.connect(onViewSlidingIn) | ||
131 | 127 | |||
132 | 128 | view.y = 0 | ||
133 | 129 | handleNewViewAdded(view) | ||
134 | 130 | newViewCreated(view.url) | ||
135 | 131 | } | ||
136 | 132 | function updateViewVisibility(view, visible) { | ||
137 | 133 | if (view) { | ||
138 | 134 | view.opacity = visible ? 1.0 : 0.0 | ||
139 | 135 | } | ||
140 | 136 | } | ||
141 | 137 | |||
142 | 138 | Component { | ||
143 | 139 | id: popupWebOverlayFactory | ||
144 | 140 | PopupWindowOverlay { | ||
145 | 141 | id: overlay | ||
146 | 142 | |||
147 | 143 | height: parent.height | ||
148 | 144 | width: parent.width | ||
149 | 145 | |||
150 | 146 | y: overlay.parent.height | ||
151 | 147 | |||
152 | 148 | // Poor mans heuristic to know when an overlay has been | ||
153 | 149 | // loaded and is in full view. We cannot rely on the | ||
154 | 150 | // NumberAnimation running/started since they dont | ||
155 | 151 | // work properly when inside a Behavior | ||
156 | 152 | onYChanged: { | ||
157 | 153 | if (y === 0) { | ||
158 | 154 | windowOverlayOpenAnimationDone() | ||
159 | 155 | } | ||
160 | 156 | } | ||
161 | 157 | |||
162 | 158 | Behavior on y { | ||
163 | 159 | NumberAnimation { | ||
164 | 160 | duration: 500 | ||
165 | 161 | easing.type: Easing.InOutQuad | ||
166 | 162 | } | ||
167 | 163 | } | ||
168 | 164 | } | ||
169 | 165 | } | ||
170 | 166 | |||
171 | 167 | function handleNewForegroundNavigationRequest( | ||
172 | 168 | url, request, isRequestFromMainWebappWebview) { | ||
173 | 169 | |||
174 | 170 | if (views.length >= maxSimultaneousViews) { | ||
175 | 171 | request.action = Oxide.NavigationRequest.ActionReject | ||
176 | 172 | // Default to open externally, maybe should present a dialog | ||
177 | 173 | openUrlExternally(url.toString()) | ||
178 | 174 | console.log("Maximum number of popup overlay opened, opening: " | ||
179 | 175 | + url | ||
180 | 176 | + " in the browser") | ||
181 | 177 | return | ||
182 | 178 | } | ||
183 | 179 | request.action = Oxide.NavigationRequest.ActionAccept | ||
184 | 180 | } | ||
185 | 181 | } | ||
186 | 0 | 182 | ||
187 | === added file 'src/app/webcontainer/PopupWindowOverlay.qml' | |||
188 | --- src/app/webcontainer/PopupWindowOverlay.qml 1970-01-01 00:00:00 +0000 | |||
189 | +++ src/app/webcontainer/PopupWindowOverlay.qml 2015-04-30 16:33:24 +0000 | |||
190 | @@ -0,0 +1,199 @@ | |||
191 | 1 | /* | ||
192 | 2 | * Copyright 2014 Canonical Ltd. | ||
193 | 3 | * | ||
194 | 4 | * This file is part of webbrowser-app. | ||
195 | 5 | * | ||
196 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
197 | 7 | * it under the terms of the GNU General Public License as published by | ||
198 | 8 | * the Free Software Foundation; version 3. | ||
199 | 9 | * | ||
200 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
201 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
202 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
203 | 13 | * GNU General Public License for more details. | ||
204 | 14 | * | ||
205 | 15 | * You should have received a copy of the GNU General Public License | ||
206 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
207 | 17 | */ | ||
208 | 18 | |||
209 | 19 | import QtQuick 2.0 | ||
210 | 20 | import QtQuick.Window 2.0 | ||
211 | 21 | import com.canonical.Oxide 1.4 as Oxide | ||
212 | 22 | import Ubuntu.Components 1.1 | ||
213 | 23 | import ".." | ||
214 | 24 | |||
215 | 25 | Item { | ||
216 | 26 | id: popup | ||
217 | 27 | |||
218 | 28 | property var popupWindowController | ||
219 | 29 | property var webContext | ||
220 | 30 | property alias request: popupWebview.request | ||
221 | 31 | property alias url: popupWebview.url | ||
222 | 32 | |||
223 | 33 | Rectangle { | ||
224 | 34 | color: "#F2F1F0" | ||
225 | 35 | anchors.fill: parent | ||
226 | 36 | } | ||
227 | 37 | |||
228 | 38 | Item { | ||
229 | 39 | id: menubar | ||
230 | 40 | |||
231 | 41 | height: units.gu(6) | ||
232 | 42 | width: parent.width | ||
233 | 43 | |||
234 | 44 | anchors { | ||
235 | 45 | top: parent.top | ||
236 | 46 | horizontalCenter: parent.horizontalCenter | ||
237 | 47 | } | ||
238 | 48 | |||
239 | 49 | ChromeButton { | ||
240 | 50 | id: closeButton | ||
241 | 51 | objectName: "overlayCloseButton" | ||
242 | 52 | anchors { | ||
243 | 53 | left: parent.left | ||
244 | 54 | verticalCenter: parent.verticalCenter | ||
245 | 55 | } | ||
246 | 56 | |||
247 | 57 | height: parent.height | ||
248 | 58 | width: height | ||
249 | 59 | |||
250 | 60 | iconName: "dropdown-menu" | ||
251 | 61 | iconSize: 0.6 * height | ||
252 | 62 | |||
253 | 63 | enabled: true | ||
254 | 64 | visible: true | ||
255 | 65 | |||
256 | 66 | MouseArea { | ||
257 | 67 | anchors.fill: parent | ||
258 | 68 | onClicked: { | ||
259 | 69 | if (popupWindowController) { | ||
260 | 70 | popupWindowController.handleViewRemoved(popup) | ||
261 | 71 | } | ||
262 | 72 | } | ||
263 | 73 | } | ||
264 | 74 | } | ||
265 | 75 | |||
266 | 76 | Item { | ||
267 | 77 | anchors { | ||
268 | 78 | top: parent.top | ||
269 | 79 | bottom: parent.bottom | ||
270 | 80 | left: closeButton.right | ||
271 | 81 | right: buttonOpenInBrowser.left | ||
272 | 82 | } | ||
273 | 83 | |||
274 | 84 | Label { | ||
275 | 85 | anchors { | ||
276 | 86 | rightMargin: units.gu(2) | ||
277 | 87 | verticalCenter: parent.verticalCenter | ||
278 | 88 | left: parent.left | ||
279 | 89 | right: parent.right | ||
280 | 90 | } | ||
281 | 91 | |||
282 | 92 | text: popupWebview.title ? popupWebview.title : popupWebview.url | ||
283 | 93 | elide: Text.ElideRight | ||
284 | 94 | } | ||
285 | 95 | |||
286 | 96 | MouseArea { | ||
287 | 97 | anchors.fill: parent | ||
288 | 98 | |||
289 | 99 | property int initMouseY: 0 | ||
290 | 100 | property int prevMouseY: 0 | ||
291 | 101 | |||
292 | 102 | onPressed: { | ||
293 | 103 | initMouseY = mouse.y | ||
294 | 104 | prevMouseY = initMouseY | ||
295 | 105 | } | ||
296 | 106 | onReleased: { | ||
297 | 107 | if ((prevMouseY - initMouseY) > (popup.height / 8) || | ||
298 | 108 | popup.y > popup.height/2) { | ||
299 | 109 | if (popupWindowController) { | ||
300 | 110 | popupWindowController.handleViewRemoved(popup) | ||
301 | 111 | return | ||
302 | 112 | } | ||
303 | 113 | } | ||
304 | 114 | popup.y = 0 | ||
305 | 115 | } | ||
306 | 116 | onMouseYChanged: { | ||
307 | 117 | if (popupWindowController) { | ||
308 | 118 | var diff = mouseY - initMouseY | ||
309 | 119 | prevMouseY = mouseY | ||
310 | 120 | popupWindowController.onOverlayMoved(popup, diff) | ||
311 | 121 | } | ||
312 | 122 | } | ||
313 | 123 | } | ||
314 | 124 | } | ||
315 | 125 | |||
316 | 126 | ChromeButton { | ||
317 | 127 | id: buttonOpenInBrowser | ||
318 | 128 | objectName: "overlayButtonOpenInBrowser" | ||
319 | 129 | anchors { | ||
320 | 130 | right: parent.right | ||
321 | 131 | verticalCenter: parent.verticalCenter | ||
322 | 132 | rightMargin: units.gu(1) | ||
323 | 133 | } | ||
324 | 134 | |||
325 | 135 | height: parent.height | ||
326 | 136 | width: height | ||
327 | 137 | |||
328 | 138 | iconName: "external-link" | ||
329 | 139 | iconSize: 0.6 * height | ||
330 | 140 | |||
331 | 141 | enabled: true | ||
332 | 142 | visible: true | ||
333 | 143 | |||
334 | 144 | MouseArea { | ||
335 | 145 | anchors.fill: parent | ||
336 | 146 | onClicked: { | ||
337 | 147 | if (popupWindowController) { | ||
338 | 148 | popupWindowController.handleOpenInUrlBrowserForView( | ||
339 | 149 | popupWebview.url, popup) | ||
340 | 150 | } | ||
341 | 151 | } | ||
342 | 152 | } | ||
343 | 153 | } | ||
344 | 154 | } | ||
345 | 155 | |||
346 | 156 | WebViewImpl { | ||
347 | 157 | id: popupWebview | ||
348 | 158 | |||
349 | 159 | objectName: "overlayWebview" | ||
350 | 160 | |||
351 | 161 | context: webContext | ||
352 | 162 | |||
353 | 163 | anchors { | ||
354 | 164 | bottom: parent.bottom | ||
355 | 165 | left: parent.left | ||
356 | 166 | right: parent.right | ||
357 | 167 | top: menubar.bottom | ||
358 | 168 | } | ||
359 | 169 | |||
360 | 170 | onNewViewRequested: { | ||
361 | 171 | if (popupWindowController) { | ||
362 | 172 | popupWindowController.createPopupView( | ||
363 | 173 | popup.parent, request, false, context) | ||
364 | 174 | } | ||
365 | 175 | } | ||
366 | 176 | |||
367 | 177 | function isNewForegroundWebViewDisposition(disposition) { | ||
368 | 178 | return disposition === Oxide.NavigationRequest.DispositionNewPopup || | ||
369 | 179 | disposition === Oxide.NavigationRequest.DispositionNewForegroundTab; | ||
370 | 180 | } | ||
371 | 181 | |||
372 | 182 | onNavigationRequested: { | ||
373 | 183 | var url = request.url.toString() | ||
374 | 184 | if (isNewForegroundWebViewDisposition(request.disposition)) { | ||
375 | 185 | popupWindowController.handleNewForegroundNavigationRequest( | ||
376 | 186 | url, request, false) | ||
377 | 187 | return | ||
378 | 188 | } | ||
379 | 189 | request.action = Oxide.NavigationRequest.ActionAccept | ||
380 | 190 | } | ||
381 | 191 | |||
382 | 192 | onCloseRequested: { | ||
383 | 193 | if (popupWindowController) { | ||
384 | 194 | popupWindowController.handleViewRemoved(popup) | ||
385 | 195 | } | ||
386 | 196 | } | ||
387 | 197 | } | ||
388 | 198 | |||
389 | 199 | } | ||
390 | 0 | 200 | ||
391 | === modified file 'src/app/webcontainer/WebViewImplOxide.qml' | |||
392 | --- src/app/webcontainer/WebViewImplOxide.qml 2015-02-19 11:50:21 +0000 | |||
393 | +++ src/app/webcontainer/WebViewImplOxide.qml 2015-04-30 16:33:24 +0000 | |||
394 | @@ -35,9 +35,13 @@ | |||
395 | 35 | property var webappUrlPatterns: null | 35 | property var webappUrlPatterns: null |
396 | 36 | property string popupRedirectionUrlPrefixPattern: "" | 36 | property string popupRedirectionUrlPrefixPattern: "" |
397 | 37 | property url dataPath | 37 | property url dataPath |
398 | 38 | property var popupController | ||
399 | 39 | property var overlayViewsParent: webview.parent | ||
400 | 38 | 40 | ||
401 | 39 | // Mostly used for testing & avoid external urls to | 41 | // Mostly used for testing & avoid external urls to |
403 | 40 | // "leak" in the default browser | 42 | // "leak" in the default browser. External URLs corresponds |
404 | 43 | // to URLs that are not included in the set defined by the url patterns | ||
405 | 44 | // (if any) or navigations resulting in new windows being created. | ||
406 | 41 | property bool blockOpenExternalUrls: false | 45 | property bool blockOpenExternalUrls: false |
407 | 42 | 46 | ||
408 | 43 | // Those signals are used for testing purposes to externally | 47 | // Those signals are used for testing purposes to externally |
409 | @@ -55,6 +59,10 @@ | |||
410 | 55 | 59 | ||
411 | 56 | preferences.allowFileAccessFromFileUrls: runningLocalApplication | 60 | preferences.allowFileAccessFromFileUrls: runningLocalApplication |
412 | 57 | preferences.allowUniversalAccessFromFileUrls: runningLocalApplication | 61 | preferences.allowUniversalAccessFromFileUrls: runningLocalApplication |
413 | 62 | preferences.localStorageEnabled: true | ||
414 | 63 | preferences.appCacheEnabled: true | ||
415 | 64 | |||
416 | 65 | onNewViewRequested: popupController.createPopupView(overlayViewsParent, request, true, context) | ||
417 | 58 | 66 | ||
418 | 59 | contextualActions: ActionList { | 67 | contextualActions: ActionList { |
419 | 60 | Actions.CopyLink { | 68 | Actions.CopyLink { |
420 | @@ -119,77 +127,17 @@ | |||
421 | 119 | } | 127 | } |
422 | 120 | 128 | ||
423 | 121 | function navigationRequestedDelegate(request) { | 129 | function navigationRequestedDelegate(request) { |
424 | 122 | var newForegroundPageRequest = isNewForegroundWebViewDisposition(request.disposition) | ||
425 | 123 | var url = request.url.toString() | 130 | var url = request.url.toString() |
426 | 124 | |||
427 | 125 | console.log("navigationRequestedDelegate - newForegroundPageRequest: " | ||
428 | 126 | + newForegroundPageRequest | ||
429 | 127 | + ', url: ' + url) | ||
430 | 128 | |||
431 | 129 | if (runningLocalApplication && url.indexOf("file://") !== 0) { | 131 | if (runningLocalApplication && url.indexOf("file://") !== 0) { |
432 | 130 | request.action = Oxide.NavigationRequest.ActionReject | 132 | request.action = Oxide.NavigationRequest.ActionReject |
433 | 131 | openUrlExternally(url) | 133 | openUrlExternally(url) |
434 | 132 | return | 134 | return |
435 | 133 | } | 135 | } |
436 | 134 | 136 | ||
495 | 135 | // Covers some edge cases corresponding to the default window.open() behavior. | 137 | request.action = Oxide.NavigationRequest.ActionReject |
496 | 136 | // When it is being called, the targetted URL will not load right away but | 138 | if (isNewForegroundWebViewDisposition(request.disposition)) { |
497 | 137 | // will first round trip to an "about:blank". | 139 | request.action = Oxide.NavigationRequest.ActionAccept |
498 | 138 | // See https://developer.mozilla.org/en-US/docs/Web/API/Window.open | 140 | popupController.handleNewForegroundNavigationRequest(url, request, true) |
441 | 139 | if (newForegroundPageRequest) { | ||
442 | 140 | if (url == 'about:blank') { | ||
443 | 141 | console.log('Accepting a new window request to navigate to "about:blank"') | ||
444 | 142 | request.action = Oxide.NavigationRequest.ActionAccept | ||
445 | 143 | return | ||
446 | 144 | } | ||
447 | 145 | |||
448 | 146 | var isRedirectionUrl = false; | ||
449 | 147 | var targetUrl = url; | ||
450 | 148 | if (popupRedirectionUrlPrefixPattern) { | ||
451 | 149 | // NOTE: very nasty workaround to be backward compatibility, will be deleted as soon | ||
452 | 150 | // as the FB webapp is updated. | ||
453 | 151 | if (popupRedirectionUrlPrefixPattern.indexOf('(') === -1) { | ||
454 | 152 | isRedirectionUrl = (url.indexOf(popupRedirectionUrlPrefixPattern) === 0); | ||
455 | 153 | targetUrl = isRedirectionUrl ? | ||
456 | 154 | url.slice(popupRedirectionUrlPrefixPattern.length) : url; | ||
457 | 155 | |||
458 | 156 | // Quick fix for http://pad.lv/1358622 (trim trailing parameters). | ||
459 | 157 | var extraParams = targetUrl.indexOf("&"); | ||
460 | 158 | if (extraParams !== -1) { | ||
461 | 159 | targetUrl = targetUrl.slice(0, extraParams); | ||
462 | 160 | } | ||
463 | 161 | } else { | ||
464 | 162 | var redirectionPatternMatch = url.match(popupRedirectionUrlPrefixPattern); | ||
465 | 163 | isRedirectionUrl = | ||
466 | 164 | popupRedirectionUrlPrefixPattern | ||
467 | 165 | && redirectionPatternMatch | ||
468 | 166 | && redirectionPatternMatch.length >= 2; | ||
469 | 167 | |||
470 | 168 | // Assume that the first group is the matching one | ||
471 | 169 | targetUrl = isRedirectionUrl ? | ||
472 | 170 | redirectionPatternMatch[1] : url; | ||
473 | 171 | } | ||
474 | 172 | } | ||
475 | 173 | |||
476 | 174 | if (isRedirectionUrl) { | ||
477 | 175 | console.debug("Got a redirection URL with target URL: " + targetUrl) | ||
478 | 176 | targetUrl = decodeURIComponent(targetUrl) | ||
479 | 177 | gotRedirectionUrl(targetUrl) | ||
480 | 178 | } | ||
481 | 179 | |||
482 | 180 | if (webview.shouldAllowNavigationTo(targetUrl)) { | ||
483 | 181 | console.debug('Redirecting popup browsing ' + targetUrl + ' in the current container window.') | ||
484 | 182 | request.action = Oxide.NavigationRequest.ActionReject | ||
485 | 183 | webappContainerHelper.browseToUrlRequested(webview, targetUrl) | ||
486 | 184 | return | ||
487 | 185 | } | ||
488 | 186 | |||
489 | 187 | if (shouldOpenPopupsInDefaultBrowser()) { | ||
490 | 188 | console.debug('Opening popup window ' + targetUrl + ' in the browser window.') | ||
491 | 189 | request.action = Oxide.NavigationRequest.ActionReject | ||
492 | 190 | openUrlExternally(targetUrl) | ||
493 | 191 | return; | ||
494 | 192 | } | ||
499 | 193 | return | 141 | return |
500 | 194 | } | 142 | } |
501 | 195 | 143 | ||
502 | @@ -201,7 +149,6 @@ | |||
503 | 201 | return | 149 | return |
504 | 202 | } | 150 | } |
505 | 203 | 151 | ||
506 | 204 | request.action = Oxide.NavigationRequest.ActionReject | ||
507 | 205 | if (webview.shouldAllowNavigationTo(url)) | 152 | if (webview.shouldAllowNavigationTo(url)) |
508 | 206 | request.action = Oxide.NavigationRequest.ActionAccept | 153 | request.action = Oxide.NavigationRequest.ActionAccept |
509 | 207 | 154 | ||
510 | @@ -228,70 +175,6 @@ | |||
511 | 228 | } | 175 | } |
512 | 229 | } | 176 | } |
513 | 230 | 177 | ||
514 | 231 | function createPopupWindow(request) { | ||
515 | 232 | popupWebViewFactory.createObject(webview, { request: request, width: 500, height: 800 }); | ||
516 | 233 | } | ||
517 | 234 | |||
518 | 235 | Component { | ||
519 | 236 | id: popupWebViewFactory | ||
520 | 237 | Window { | ||
521 | 238 | id: popup | ||
522 | 239 | property alias request: popupBrowser.request | ||
523 | 240 | WebView { | ||
524 | 241 | id: popupBrowser | ||
525 | 242 | anchors.fill: parent | ||
526 | 243 | |||
527 | 244 | function navigationRequestedDelegate(request) { | ||
528 | 245 | var url = request.url.toString() | ||
529 | 246 | |||
530 | 247 | // If we are to browse in the popup to a place where we are not allows | ||
531 | 248 | if ( ! isNewForegroundWebViewDisposition(request.disposition) && | ||
532 | 249 | ! webview.shouldAllowNavigationTo(url)) { | ||
533 | 250 | request.action = Oxide.NavigationRequest.ActionReject | ||
534 | 251 | openUrlExternally(url); | ||
535 | 252 | popup.close() | ||
536 | 253 | return; | ||
537 | 254 | } | ||
538 | 255 | |||
539 | 256 | // Fallback to regulat checks (there is a bit of overlap) | ||
540 | 257 | webview.navigationRequestedDelegate(request) | ||
541 | 258 | } | ||
542 | 259 | |||
543 | 260 | onNewViewRequested: webview.createPopupWindow(request) | ||
544 | 261 | |||
545 | 262 | // Oxide (and Chromium) does not inform of non user | ||
546 | 263 | // driven navigations (or more specifically redirects that | ||
547 | 264 | // would be part of an popup/webview load (after its been | ||
548 | 265 | // granted). Quite a few sites (e.g. Youtube), | ||
549 | 266 | // create popups when clicking on links (or following a window.open()) | ||
550 | 267 | // with proper youtube.com address but w/ redirection | ||
551 | 268 | // params, e.g.: | ||
552 | 269 | // http://www.youtube.com/redirect?q=http%3A%2F%2Fgodzillamovie.com%2F&redir_token=b8WPI1pq9FHXeHm2bN3KVLAJSfp8MTM5NzI2NDg3NEAxMzk3MTc4NDc0 | ||
553 | 270 | // In this instance the popup & navigation is granted, but then | ||
554 | 271 | // a redirect happens inside the popup to the real target url (here http://godzillamovie.com) | ||
555 | 272 | // which is not trapped by a navigation requested and therefore not filtered. | ||
556 | 273 | // The only way to do it atm is to listen to url changed in popups & also | ||
557 | 274 | // filter there. | ||
558 | 275 | onUrlChanged: { | ||
559 | 276 | var _url = url.toString(); | ||
560 | 277 | if (_url.trim().length === 0) | ||
561 | 278 | return; | ||
562 | 279 | |||
563 | 280 | if (_url != 'about:blank' && ! webview.shouldAllowNavigationTo(_url)) { | ||
564 | 281 | openUrlExternally(_url); | ||
565 | 282 | popup.close() | ||
566 | 283 | } | ||
567 | 284 | } | ||
568 | 285 | } | ||
569 | 286 | Component.onCompleted: popup.show() | ||
570 | 287 | } | ||
571 | 288 | } | ||
572 | 289 | |||
573 | 290 | onNewViewRequested: createPopupWindow(request) | ||
574 | 291 | |||
575 | 292 | preferences.localStorageEnabled: true | ||
576 | 293 | preferences.appCacheEnabled: true | ||
577 | 294 | |||
578 | 295 | // Small shim needed when running as a webapp to wire-up connections | 178 | // Small shim needed when running as a webapp to wire-up connections |
579 | 296 | // with the webview (message received, etc…). | 179 | // with the webview (message received, etc…). |
580 | 297 | // This is being called (and expected) internally by the webapps | 180 | // This is being called (and expected) internally by the webapps |
581 | 298 | 181 | ||
582 | === modified file 'src/app/webcontainer/WebappContainerWebview.qml' | |||
583 | --- src/app/webcontainer/WebappContainerWebview.qml 2015-03-25 18:42:20 +0000 | |||
584 | +++ src/app/webcontainer/WebappContainerWebview.qml 2015-04-30 16:33:24 +0000 | |||
585 | @@ -40,6 +40,14 @@ | |||
586 | 40 | property bool blockOpenExternalUrls: false | 40 | property bool blockOpenExternalUrls: false |
587 | 41 | property bool runningLocalApplication: false | 41 | property bool runningLocalApplication: false |
588 | 42 | 42 | ||
589 | 43 | PopupWindowController { | ||
590 | 44 | id: popupController | ||
591 | 45 | objectName: "popupController" | ||
592 | 46 | webappUrlPatterns: containerWebview.webappUrlPatterns | ||
593 | 47 | mainWebappView: containerWebview.currentWebview | ||
594 | 48 | blockOpenExternalUrls: containerWebview.blockOpenExternalUrls | ||
595 | 49 | } | ||
596 | 50 | |||
597 | 43 | Loader { | 51 | Loader { |
598 | 44 | id: webappContainerWebViewLoader | 52 | id: webappContainerWebViewLoader |
599 | 45 | objectName: "containerWebviewLoader" | 53 | objectName: "containerWebviewLoader" |
600 | @@ -68,7 +76,9 @@ | |||
601 | 68 | , developerExtrasEnabled: containerWebview.developerExtrasEnabled | 76 | , developerExtrasEnabled: containerWebview.developerExtrasEnabled |
602 | 69 | , popupRedirectionUrlPrefixPattern: containerWebview.popupRedirectionUrlPrefixPattern | 77 | , popupRedirectionUrlPrefixPattern: containerWebview.popupRedirectionUrlPrefixPattern |
603 | 70 | , blockOpenExternalUrls: containerWebview.blockOpenExternalUrls | 78 | , blockOpenExternalUrls: containerWebview.blockOpenExternalUrls |
605 | 71 | , runningLocalApplication: containerWebview.runningLocalApplication}) | 79 | , runningLocalApplication: containerWebview.runningLocalApplication |
606 | 80 | , popupController: popupController | ||
607 | 81 | , overlayViewsParent: containerWebview.parent}) | ||
608 | 72 | } | 82 | } |
609 | 73 | } | 83 | } |
610 | 74 | 84 | ||
611 | 75 | 85 | ||
612 | === modified file 'tests/autopilot/webapp_container/tests/__init__.py' | |||
613 | --- tests/autopilot/webapp_container/tests/__init__.py 2015-03-20 18:22:41 +0000 | |||
614 | +++ tests/autopilot/webapp_container/tests/__init__.py 2015-04-30 16:33:24 +0000 | |||
615 | @@ -83,6 +83,12 @@ | |||
616 | 83 | def get_webview(self): | 83 | def get_webview(self): |
617 | 84 | return self.app.select_single(objectName="webview") | 84 | return self.app.select_single(objectName="webview") |
618 | 85 | 85 | ||
619 | 86 | def get_popup_overlay_views(self): | ||
620 | 87 | return self.app.select_many("PopupWindowOverlay") | ||
621 | 88 | |||
622 | 89 | def get_popup_controller(self): | ||
623 | 90 | return self.app.select_single(objectName="popupController") | ||
624 | 91 | |||
625 | 86 | def get_oxide_webview(self): | 92 | def get_oxide_webview(self): |
626 | 87 | container = self.get_webview().select_single( | 93 | container = self.get_webview().select_single( |
627 | 88 | objectName='containerWebviewLoader') | 94 | objectName='containerWebviewLoader') |
628 | 89 | 95 | ||
629 | === modified file 'tests/autopilot/webapp_container/tests/fake_servers.py' | |||
630 | --- tests/autopilot/webapp_container/tests/fake_servers.py 2014-11-28 17:42:50 +0000 | |||
631 | +++ tests/autopilot/webapp_container/tests/fake_servers.py 2015-04-30 16:33:24 +0000 | |||
632 | @@ -66,22 +66,22 @@ | |||
633 | 66 | </html> | 66 | </html> |
634 | 67 | """ | 67 | """ |
635 | 68 | 68 | ||
640 | 69 | def targetted_click_content(self, differentDomain=True): | 69 | def targetted_click_content(self): |
637 | 70 | url = 'http://www.test.com/' | ||
638 | 71 | if differentDomain: | ||
639 | 72 | url = 'http://www.ubuntu.com/' | ||
641 | 73 | return """ | 70 | return """ |
642 | 74 | <html> | 71 | <html> |
643 | 75 | <head> | 72 | <head> |
644 | 76 | <title>Some content</title> | 73 | <title>Some content</title> |
645 | 77 | </head> | 74 | </head> |
646 | 78 | <body> | 75 | <body> |
650 | 79 | <div><a href='{}' target='_blank'> | 76 | <div> |
651 | 80 | <div style="height: 100%; width: 100%"></div> | 77 | <a href="/open-close-content" target="_blank"> |
652 | 81 | </a></div> | 78 | <div style="height: 100%; width: 100%"> |
653 | 79 | </div> | ||
654 | 80 | </a> | ||
655 | 81 | </div> | ||
656 | 82 | </body> | 82 | </body> |
657 | 83 | </html> | 83 | </html> |
659 | 84 | """.format(url) | 84 | """ |
660 | 85 | 85 | ||
661 | 86 | def display_ua_content(self): | 86 | def display_ua_content(self): |
662 | 87 | return """ | 87 | return """ |
663 | @@ -99,6 +99,27 @@ | |||
664 | 99 | </html> | 99 | </html> |
665 | 100 | """.format("'"+self.headers['user-agent']+"'") | 100 | """.format("'"+self.headers['user-agent']+"'") |
666 | 101 | 101 | ||
667 | 102 | def open_close_content(self): | ||
668 | 103 | return """ | ||
669 | 104 | <html> | ||
670 | 105 | <head> | ||
671 | 106 | <title>open-close</title> | ||
672 | 107 | <script> | ||
673 | 108 | </script> | ||
674 | 109 | </head> | ||
675 | 110 | <body> | ||
676 | 111 | <a href="/open-close-content" target="_blank"> | ||
677 | 112 | <div style="height: 50%; width: 100%; background-color: red"> | ||
678 | 113 | target blank link | ||
679 | 114 | </div> | ||
680 | 115 | </a> | ||
681 | 116 | <div id="lorem" style="height: 50%; width: 100%; background-color: blue"> | ||
682 | 117 | Lorem ipsum dolor sit amet | ||
683 | 118 | </div> | ||
684 | 119 | </body> | ||
685 | 120 | </html> | ||
686 | 121 | """ | ||
687 | 122 | |||
688 | 102 | def do_GET(self): | 123 | def do_GET(self): |
689 | 103 | if self.path == '/': | 124 | if self.path == '/': |
690 | 104 | self.send_response(200) | 125 | self.send_response(200) |
691 | @@ -114,13 +135,13 @@ | |||
692 | 114 | self.serve_content(self.external_click_content()) | 135 | self.serve_content(self.external_click_content()) |
693 | 115 | elif self.path == '/with-targetted-link': | 136 | elif self.path == '/with-targetted-link': |
694 | 116 | self.send_response(200) | 137 | self.send_response(200) |
695 | 117 | self.serve_content(self.targetted_click_content(False)) | ||
696 | 118 | elif self.path == '/with-different-targetted-link': | ||
697 | 119 | self.send_response(200) | ||
698 | 120 | self.serve_content(self.targetted_click_content()) | 138 | self.serve_content(self.targetted_click_content()) |
699 | 121 | elif self.path == '/show-user-agent': | 139 | elif self.path == '/show-user-agent': |
700 | 122 | self.send_response(200) | 140 | self.send_response(200) |
701 | 123 | self.serve_content(self.display_ua_content()) | 141 | self.serve_content(self.display_ua_content()) |
702 | 142 | elif self.path == '/open-close-content': | ||
703 | 143 | self.send_response(200) | ||
704 | 144 | self.serve_content(self.open_close_content()) | ||
705 | 124 | else: | 145 | else: |
706 | 125 | self.send_error(404) | 146 | self.send_error(404) |
707 | 126 | 147 | ||
708 | 127 | 148 | ||
709 | === added file 'tests/autopilot/webapp_container/tests/test_popup_webview_overlay.py' | |||
710 | --- tests/autopilot/webapp_container/tests/test_popup_webview_overlay.py 1970-01-01 00:00:00 +0000 | |||
711 | +++ tests/autopilot/webapp_container/tests/test_popup_webview_overlay.py 2015-04-30 16:33:24 +0000 | |||
712 | @@ -0,0 +1,170 @@ | |||
713 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
714 | 2 | # Copyright 2015 Canonical | ||
715 | 3 | # | ||
716 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
717 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
718 | 6 | # by the Free Software Foundation. | ||
719 | 7 | # | ||
720 | 8 | # This program is distributed in the hope that it will be useful, | ||
721 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
722 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
723 | 11 | # GNU General Public License for more details. | ||
724 | 12 | # | ||
725 | 13 | # You should have received a copy of the GNU General Public License | ||
726 | 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
727 | 15 | |||
728 | 16 | from testtools.matchers import Equals, Contains, GreaterThan | ||
729 | 17 | from autopilot.matchers import Eventually | ||
730 | 18 | |||
731 | 19 | from webapp_container.tests import WebappContainerTestCaseWithLocalContentBase | ||
732 | 20 | |||
733 | 21 | |||
734 | 22 | class WebappContainerPopupWebViewOverlayTestCase( | ||
735 | 23 | WebappContainerTestCaseWithLocalContentBase): | ||
736 | 24 | |||
737 | 25 | def click_href_target_blank(self): | ||
738 | 26 | webview = self.get_oxide_webview() | ||
739 | 27 | self.assertThat(webview.url, Contains('/open-close-content')) | ||
740 | 28 | gr = webview.globalRect | ||
741 | 29 | self.pointing_device.move( | ||
742 | 30 | gr.x + gr.width/4, | ||
743 | 31 | gr.y + gr.height/4) | ||
744 | 32 | self.pointing_device.click() | ||
745 | 33 | |||
746 | 34 | def click_window_open(self): | ||
747 | 35 | webview = self.get_oxide_webview() | ||
748 | 36 | self.assertThat(webview.url.endswith('/open-close-content')) | ||
749 | 37 | gr = webview.globalRect | ||
750 | 38 | self.pointing_device.move( | ||
751 | 39 | gr.x + webview.width*3/4, | ||
752 | 40 | gr.y + webview.height*3/4) | ||
753 | 41 | self.pointing_device.click() | ||
754 | 42 | |||
755 | 43 | def test_open_close_back_to_mainview(self): | ||
756 | 44 | args = [] | ||
757 | 45 | self.launch_webcontainer_app_with_local_http_server( | ||
758 | 46 | args, | ||
759 | 47 | '/open-close-content') | ||
760 | 48 | self.get_webcontainer_window().visible.wait_for(True) | ||
761 | 49 | |||
762 | 50 | popup_controller = self.get_popup_controller() | ||
763 | 51 | new_view_watcher = popup_controller.watch_signal( | ||
764 | 52 | 'newViewCreated(QString)') | ||
765 | 53 | animation_watcher = popup_controller.watch_signal( | ||
766 | 54 | 'windowOverlayOpenAnimationDone()') | ||
767 | 55 | animation_signal_emission = animation_watcher.num_emissions | ||
768 | 56 | |||
769 | 57 | views = self.get_popup_overlay_views() | ||
770 | 58 | self.assertThat(len(views), Equals(0)) | ||
771 | 59 | |||
772 | 60 | self.click_href_target_blank() | ||
773 | 61 | |||
774 | 62 | self.assertThat( | ||
775 | 63 | lambda: new_view_watcher.was_emitted, | ||
776 | 64 | Eventually(Equals(True))) | ||
777 | 65 | self.assertThat( | ||
778 | 66 | lambda: len(self.get_popup_overlay_views()), | ||
779 | 67 | Eventually(Equals(1))) | ||
780 | 68 | views = self.get_popup_overlay_views() | ||
781 | 69 | overlay = views[0] | ||
782 | 70 | self.assertThat( | ||
783 | 71 | overlay.select_single(objectName="overlayWebview").url, | ||
784 | 72 | Contains('/open-close-content')) | ||
785 | 73 | |||
786 | 74 | self.assertThat( | ||
787 | 75 | lambda: animation_watcher.num_emissions, | ||
788 | 76 | Eventually(GreaterThan(animation_signal_emission))) | ||
789 | 77 | animation_signal_emission = animation_watcher.num_emissions | ||
790 | 78 | |||
791 | 79 | closeButton = overlay.select_single( | ||
792 | 80 | objectName='overlayCloseButton') | ||
793 | 81 | |||
794 | 82 | self.pointing_device.click_object(closeButton) | ||
795 | 83 | |||
796 | 84 | self.assertThat( | ||
797 | 85 | lambda: len(self.get_popup_overlay_views()), | ||
798 | 86 | Eventually(Equals(0))) | ||
799 | 87 | |||
800 | 88 | def test_open_overlay_in_main_browser(self): | ||
801 | 89 | args = [] | ||
802 | 90 | self.launch_webcontainer_app_with_local_http_server( | ||
803 | 91 | args, | ||
804 | 92 | '/open-close-content', | ||
805 | 93 | {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1'}) | ||
806 | 94 | self.get_webcontainer_window().visible.wait_for(True) | ||
807 | 95 | |||
808 | 96 | popup_controller = self.get_popup_controller() | ||
809 | 97 | webview = self.get_oxide_webview() | ||
810 | 98 | self.assertThat( | ||
811 | 99 | lambda: webview.visible, | ||
812 | 100 | Eventually(Equals(True))) | ||
813 | 101 | external_open_watcher = popup_controller.watch_signal( | ||
814 | 102 | 'openExternalUrlTriggered(QString)') | ||
815 | 103 | |||
816 | 104 | animation_watcher = popup_controller.watch_signal( | ||
817 | 105 | 'windowOverlayOpenAnimationDone()') | ||
818 | 106 | animation_signal_emission = animation_watcher.num_emissions | ||
819 | 107 | |||
820 | 108 | self.click_href_target_blank() | ||
821 | 109 | |||
822 | 110 | self.assertThat( | ||
823 | 111 | lambda: len(self.get_popup_overlay_views()), | ||
824 | 112 | Eventually(Equals(1))) | ||
825 | 113 | |||
826 | 114 | views = self.get_popup_overlay_views() | ||
827 | 115 | overlay = views[0] | ||
828 | 116 | |||
829 | 117 | self.assertThat( | ||
830 | 118 | lambda: animation_watcher.num_emissions, | ||
831 | 119 | Eventually(GreaterThan(animation_signal_emission))) | ||
832 | 120 | animation_signal_emission = animation_watcher.num_emissions | ||
833 | 121 | |||
834 | 122 | openInBrowserButton = overlay.select_single( | ||
835 | 123 | objectName='overlayButtonOpenInBrowser') | ||
836 | 124 | |||
837 | 125 | self.pointing_device.click_object(openInBrowserButton) | ||
838 | 126 | |||
839 | 127 | self.assertThat( | ||
840 | 128 | lambda: len(self.get_popup_overlay_views()), | ||
841 | 129 | Eventually(Equals(0))) | ||
842 | 130 | self.assertThat( | ||
843 | 131 | lambda: external_open_watcher.was_emitted, | ||
844 | 132 | Eventually(Equals(True))) | ||
845 | 133 | self.assertThat( | ||
846 | 134 | lambda: webview.visible, | ||
847 | 135 | Eventually(Equals(True))) | ||
848 | 136 | |||
849 | 137 | def test_max_overlay_count_reached(self): | ||
850 | 138 | args = [] | ||
851 | 139 | self.launch_webcontainer_app_with_local_http_server( | ||
852 | 140 | args, | ||
853 | 141 | '/open-close-content', | ||
854 | 142 | {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1'}) | ||
855 | 143 | self.get_webcontainer_window().visible.wait_for(True) | ||
856 | 144 | |||
857 | 145 | popup_controller = self.get_popup_controller() | ||
858 | 146 | webview = self.get_oxide_webview() | ||
859 | 147 | self.assertThat( | ||
860 | 148 | lambda: webview.visible, | ||
861 | 149 | Eventually(Equals(True))) | ||
862 | 150 | |||
863 | 151 | animation_watcher = popup_controller.watch_signal( | ||
864 | 152 | 'windowOverlayOpenAnimationDone()') | ||
865 | 153 | animation_signal_emission = animation_watcher.num_emissions | ||
866 | 154 | |||
867 | 155 | OVERLAY_MAX_COUNT = 3 | ||
868 | 156 | for i in range(0, OVERLAY_MAX_COUNT): | ||
869 | 157 | self.click_href_target_blank() | ||
870 | 158 | self.assertThat( | ||
871 | 159 | lambda: animation_watcher.num_emissions, | ||
872 | 160 | Eventually(GreaterThan(animation_signal_emission))) | ||
873 | 161 | animation_signal_emission = animation_watcher.num_emissions | ||
874 | 162 | |||
875 | 163 | external_open_watcher = popup_controller.watch_signal( | ||
876 | 164 | 'openExternalUrlTriggered(QString)') | ||
877 | 165 | |||
878 | 166 | self.click_href_target_blank() | ||
879 | 167 | |||
880 | 168 | self.assertThat( | ||
881 | 169 | lambda: external_open_watcher.was_emitted, | ||
882 | 170 | Eventually(Equals(True))) | ||
883 | 0 | 171 | ||
884 | === removed file 'tests/autopilot/webapp_container/tests/test_redirection_pattern.py' | |||
885 | --- tests/autopilot/webapp_container/tests/test_redirection_pattern.py 2014-10-07 16:16:13 +0000 | |||
886 | +++ tests/autopilot/webapp_container/tests/test_redirection_pattern.py 1970-01-01 00:00:00 +0000 | |||
887 | @@ -1,59 +0,0 @@ | |||
888 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
889 | 2 | # Copyright 2014 Canonical | ||
890 | 3 | # | ||
891 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
892 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
893 | 6 | # by the Free Software Foundation. | ||
894 | 7 | # | ||
895 | 8 | # This program is distributed in the hope that it will be useful, | ||
896 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
897 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
898 | 11 | # GNU General Public License for more details. | ||
899 | 12 | # | ||
900 | 13 | # You should have received a copy of the GNU General Public License | ||
901 | 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
902 | 15 | |||
903 | 16 | from testtools.matchers import Equals | ||
904 | 17 | from autopilot.matchers import Eventually | ||
905 | 18 | |||
906 | 19 | from webapp_container.tests import WebappContainerTestCaseWithLocalContentBase | ||
907 | 20 | |||
908 | 21 | |||
909 | 22 | class WebappContainerRedirectionPatternTestCase( | ||
910 | 23 | WebappContainerTestCaseWithLocalContentBase): | ||
911 | 24 | |||
912 | 25 | def test_browse_to_redirection_pattern_url(self): | ||
913 | 26 | REDIRECTION_HOSTNAME = self.get_base_url_hostname() | ||
914 | 27 | args = ["--popup-redirection-url-prefix={}{}{}".format( | ||
915 | 28 | 'http://', REDIRECTION_HOSTNAME.replace('.', '\.'), | ||
916 | 29 | '/redirect\\?url=([^&]*).*')] | ||
917 | 30 | self.launch_webcontainer_app_with_local_http_server( | ||
918 | 31 | args, | ||
919 | 32 | '/get-redirect', | ||
920 | 33 | {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1', | ||
921 | 34 | 'WEBAPP_CONTAINER_DO_NOT_FILTER_PATTERN_URL': '1'}) | ||
922 | 35 | self.get_webcontainer_window().visible.wait_for(True) | ||
923 | 36 | |||
924 | 37 | webview = self.get_oxide_webview() | ||
925 | 38 | external_open_watcher = webview.watch_signal( | ||
926 | 39 | 'openExternalUrlTriggered(QString)') | ||
927 | 40 | got_redirection_url_watcher = webview.watch_signal( | ||
928 | 41 | 'gotRedirectionUrl(QString)') | ||
929 | 42 | |||
930 | 43 | self.assertThat(external_open_watcher.was_emitted, Equals(False)) | ||
931 | 44 | self.assertThat(got_redirection_url_watcher.was_emitted, Equals(False)) | ||
932 | 45 | self.browse_to( | ||
933 | 46 | "http://{}/get-redirect".format(REDIRECTION_HOSTNAME)) | ||
934 | 47 | |||
935 | 48 | self.pointing_device.click_object(webview) | ||
936 | 49 | |||
937 | 50 | self.assertThat( | ||
938 | 51 | lambda: got_redirection_url_watcher.was_emitted, | ||
939 | 52 | Eventually(Equals(True))) | ||
940 | 53 | self.assertThat( | ||
941 | 54 | webview.get_signal_emissions( | ||
942 | 55 | 'gotRedirectionUrl(QString)')[0][0], | ||
943 | 56 | Equals('myredirect')) | ||
944 | 57 | self.assertThat( | ||
945 | 58 | lambda: external_open_watcher.was_emitted, | ||
946 | 59 | Eventually(Equals(True))) | ||
947 | 60 | 0 | ||
948 | === modified file 'tests/autopilot/webapp_container/tests/test_url_patterns.py' | |||
949 | --- tests/autopilot/webapp_container/tests/test_url_patterns.py 2015-02-18 11:05:35 +0000 | |||
950 | +++ tests/autopilot/webapp_container/tests/test_url_patterns.py 2015-04-30 16:33:24 +0000 | |||
951 | @@ -43,55 +43,3 @@ | |||
952 | 43 | self.assertThat( | 43 | self.assertThat( |
953 | 44 | lambda: external_open_watcher.was_emitted, | 44 | lambda: external_open_watcher.was_emitted, |
954 | 45 | Eventually(Equals(True))) | 45 | Eventually(Equals(True))) |
955 | 46 | |||
956 | 47 | def test_pattern_with_allowed_targetted_url(self): | ||
957 | 48 | args = ["--webappUrlPatterns=http://www.test.com/*"] | ||
958 | 49 | rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() | ||
959 | 50 | self.launch_webcontainer_app_with_local_http_server( | ||
960 | 51 | args, | ||
961 | 52 | '', | ||
962 | 53 | {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1', | ||
963 | 54 | 'UBUNTU_WEBVIEW_HOST_MAPPING_RULES': rule}, | ||
964 | 55 | "http://www.test.com/with-targetted-link") | ||
965 | 56 | |||
966 | 57 | self.get_webcontainer_window().visible.wait_for(True) | ||
967 | 58 | |||
968 | 59 | webview = self.get_oxide_webview() | ||
969 | 60 | external_open_watcher = webview.watch_signal( | ||
970 | 61 | 'openExternalUrlTriggered(QString)') | ||
971 | 62 | |||
972 | 63 | self.pointing_device.click_object(webview) | ||
973 | 64 | |||
974 | 65 | self.assertThat( | ||
975 | 66 | webview.url, | ||
976 | 67 | Eventually(Equals("http://www.test.com/"))) | ||
977 | 68 | |||
978 | 69 | self.assertThat( | ||
979 | 70 | external_open_watcher.was_emitted, | ||
980 | 71 | Equals(False)) | ||
981 | 72 | |||
982 | 73 | def test_pattern_with_different_targetted_url(self): | ||
983 | 74 | args = ["--webappUrlPatterns=http://www.test.com/*"] | ||
984 | 75 | rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() | ||
985 | 76 | self.launch_webcontainer_app_with_local_http_server( | ||
986 | 77 | args, | ||
987 | 78 | '', | ||
988 | 79 | {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1', | ||
989 | 80 | 'UBUNTU_WEBVIEW_HOST_MAPPING_RULES': rule}, | ||
990 | 81 | "http://www.test.com/with-different-targetted-link") | ||
991 | 82 | |||
992 | 83 | self.get_webcontainer_window().visible.wait_for(True) | ||
993 | 84 | |||
994 | 85 | webview = self.get_oxide_webview() | ||
995 | 86 | external_open_watcher = webview.watch_signal( | ||
996 | 87 | 'openExternalUrlTriggered(QString)') | ||
997 | 88 | |||
998 | 89 | self.pointing_device.click_object(webview) | ||
999 | 90 | |||
1000 | 91 | self.assertThat( | ||
1001 | 92 | webview.url, | ||
1002 | 93 | Equals("http://www.test.com/with-different-targetted-link")) | ||
1003 | 94 | |||
1004 | 95 | self.assertThat( | ||
1005 | 96 | lambda: external_open_watcher.was_emitted, | ||
1006 | 97 | Eventually(Equals(True))) |
FAILED: Continuous integration, rev:943 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 1552/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 1930/console jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 310/console jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 310/console jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 310/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 1928/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 1552/rebuild
http://