Merge lp:~abreu-alexandre/webbrowser-app/container-handle-context-menu into lp:webbrowser-app

Proposed by Alexandre Abreu
Status: Merged
Approved by: Alberto Mardegan
Approved revision: 1298
Merged at revision: 1318
Proposed branch: lp:~abreu-alexandre/webbrowser-app/container-handle-context-menu
Merge into: lp:webbrowser-app
Diff against target: 904 lines (+764/-3)
9 files modified
src/app/actions/OpenLinkInWebBrowser.qml (+23/-0)
src/app/webcontainer/ContextMenuMobile.qml (+157/-0)
src/app/webcontainer/ContextMenuWide.qml (+142/-0)
src/app/webcontainer/WebApp.qml (+1/-0)
src/app/webcontainer/WebViewImplOxide.qml (+56/-2)
src/app/webcontainer/WebappContainerWebview.qml (+5/-1)
src/app/webcontainer/assets/stock_link.svg (+164/-0)
tests/autopilot/webapp_container/tests/fake_servers.py (+27/-0)
tests/autopilot/webapp_container/tests/test_context_menu.py (+189/-0)
To merge this branch: bzr merge lp:~abreu-alexandre/webbrowser-app/container-handle-context-menu
Reviewer Review Type Date Requested Status
Alberto Mardegan (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+274636@code.launchpad.net

Commit message

Limited handling of custom context menu for the container

Description of the change

Limited handling of custom context menu for the container.

I duplicated the mobile context menu w/o the asset usage so that the webbrowser-app is not touched and the changes are limited. This is of course not a definite dialog,

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1236. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

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

This fixes the issue for the webapp container indeed, but it doesn’t for the general Ubuntu WebView case: apps embedding a WebView and using the default context menu will still get the crash, as you remarked in https://bugs.launchpad.net/ubuntu/+source/webbrowser-app/+bug/1501330/comments/11.

I have submitted https://code.launchpad.net/~osomon/webbrowser-app/default-context-menu-crash-on-scroll/+merge/274671 which fixes the bug in the Ubuntu WebView. Let’s update the webapp container’s context menu in a separate branch (but let’s do it asap for OTA8).

1237. By Arthur Mello

Add bookmarks view to top level menu
Approved by: Ugo Riboni, Olivier Tilloy

1238. By Olivier Tilloy

Ensure that the default context menu cannot be dismissed several times, causing a crash in oxide.
The issue in oxide remains, it should have a safeguard against this kind of behaviour. Fixes: #1501330
Approved by: Alexandre Abreu

1239. By Olivier Tilloy

Update a localized error string, per design. Fixes: #1493466
Approved by: Bartosz Kosiorek

1240. By Olivier Tilloy

Fixes for minor UX issues with the preview grid:
 - Make the highlight follow the item selected for deletion in the preview grid.
 - Do not show a highlight in the narrow version of the new tab view, as it doesn’t support keyboard navigation.
 - Grab dismiss area events so that they are not propagated to the view below when dismissing a context menu. Fixes: #1506579

1241. By Olivier Tilloy

Fix a couple of autopilot failures on desktop in narrow mode. Fixes: #1506879

1242. By CI Train Bot Account

Releasing 0.23+15.10.20151016-0ubuntu1

1243. By CI Train Bot Account

Resync trunk.

1244. By Olivier Tilloy

Update translation template.

1245. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1246. By Ugo Riboni

Implement support for allowing or denying access to media input devices and for setting default media input devices. Fixes: #1410996
Approved by: PS Jenkins bot, Olivier Tilloy

1247. By Ugo Riboni

Refactor the BookmarksModel to be a singleton.
Approved by: PS Jenkins bot

1248. By Ugo Riboni

Fix inability to drag the map to pan in Google maps, on desktop. Fixes: #1503506
Approved by: PS Jenkins bot, Olivier Tilloy

1249. By Olivier Tilloy

Add an exception to the generated apparmor profile to allow reading HERE’s TOS in the browser. Fixes: #1507667
Approved by: PS Jenkins bot, Olivier Tilloy

1250. By Olivier Tilloy

Modify the generated apparmor profile to allow rw access to /dev/shm/.org.chromium.Chromium.* too. Fixes: #1508054
Approved by: PS Jenkins bot

1251. By CI Train Bot Account

Releasing 0.23+15.10.20151022.1-0ubuntu1

1252. By CI Train Bot Account

Resync trunk.

1253. By Olivier Tilloy

Update translation template.

1254. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1255. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1256. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1257. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1258. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1259. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1260. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1261. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1262. By Ugo Riboni

Focus the page when opening links with target="_blank". Fixes: #1505724
Approved by: Olivier Tilloy, PS Jenkins bot

1263. By Ugo Riboni

Use AbstractButton to handle some tab interactions so that haptics are used on touch. Fixes: #1505725
Approved by: Olivier Tilloy, PS Jenkins bot

1264. By Ugo Riboni

Fix a bug preventing in some cases the address bar from getting focus when entering find in page mode. Also correctly hide the left hand side icons when in find in page mode. Fixes: #1508130
Approved by: Olivier Tilloy, PS Jenkins bot

1265. By Ugo Riboni

Prevent the address bar from being cleared when the actual url changes and the user has already started typing. Fixes: #1487713
Approved by: Olivier Tilloy

1266. By Ugo Riboni

Update the user script to remove the Facebook app banner. Fixes: #1352783
Approved by: Olivier Tilloy, PS Jenkins bot

1267. By CI Train Bot Account

Releasing 0.23+16.04.20151103-0ubuntu1

1268. By CI Train Bot Account

Resync trunk.

1269. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1270. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1271. By Ugo Riboni

Add keyboard shortcuts to undo closing tabs (Ctrl+Shift+T and Ctrl+Shift+W). Fixes: #1499767
Approved by: PS Jenkins bot, Olivier Tilloy

1272. By Ugo Riboni

Hide webviews created via onNewViewRequested until the tab they belong to becomes current. Fixes: #1464436
Approved by: PS Jenkins bot

1273. By Olivier Tilloy

Ship empty apparmor hardware profile directories to avoid adding a runtime dependency on apparmor-easyprof-ubuntu. Fixes: #1511439
Approved by: PS Jenkins bot, Jamie Strandboge

1274. By Ugo Riboni

Use lowercase letters for keyboard shortcut invokation in autopilot tests.

Autopilot tries to be too clever and automatically issues a Shift when an uppercase letter is used in a keypress combo. Fix our tests to adjust to this behavior.
Approved by: PS Jenkins bot, Olivier Tilloy

1275. By Olivier Tilloy

Add missing unit test cleanup.
Approved by: PS Jenkins bot

1276. By Ugo Riboni

Create the webview in a safer way, by keeping the incubator around and monitoring the progress, and by using sync creation if there is a pending request. Fixes: #1514701
Approved by: PS Jenkins bot

1277. By CI Train Bot Account

Releasing 0.23+16.04.20151111.2-0ubuntu1

1278. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1279. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1280. By Olivier Tilloy

Multiple changes to improve the tab switching animation.
The biggest remaining issue is bug #1502675, which is worked around here until it is properly addressed in oxide.
Approved by: PS Jenkins bot

1281. By CI Train Bot Account

Releasing 0.23+16.04.20151124-0ubuntu1

1282. By CI Train Bot Account

Resync trunk.

1283. By Olivier Tilloy

Add missing import statements, and add missing unit tests for BookmarksView.qml and BookmarksViewWide.qml. Fixes: #1515631
Approved by: Ugo Riboni

1284. By Olivier Tilloy

Code cleanup: remove unused include statements and forward declarations, and convert unnecessary include statements to forward declarations.

1285. By Olivier Tilloy

Prevent click events from propagating through the expanded history view to the view below. Fixes: #1518904
Approved by: Ugo Riboni, PS Jenkins bot

1286. By Olivier Tilloy

Use the capture device display names instead of their unique ID. Fixes: #1518931
Approved by: Ugo Riboni, PS Jenkins bot

1287. By Olivier Tilloy

Make the drawer menu flickable so that all its actions can be accessed even on a small mobile screen in landscape orientation. Fixes: #1513348

1288. By CI Train Bot Account

Releasing 0.23+16.04.20151126-0ubuntu1

1289. By CI Train Bot Account

Resync trunk.

1290. By Olivier Tilloy

Update translation template.

1291. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1292. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1293. By CI Train Bot Account

Rebuild against Qt 5.5.1.

1294. By CI Train Bot Account

Releasing 0.23+16.04.20151130-0ubuntu1

1295. By CI Train Bot Account

Resync trunk.

1296. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1297. 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
Alberto Mardegan (mardy) wrote :

Looks good, just please revert the change to src/app/webcontainer/local-cookie-store.cpp and ping me to approve it :-)

review: Needs Fixing
1298. By Alexandre Abreu

Handle context menu

Revision history for this message
Alberto Mardegan (mardy) wrote :

LGTM!

review: Approve
1299. By Alexandre Abreu

fix small flake8 nit

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/app/actions/OpenLinkInWebBrowser.qml'
2--- src/app/actions/OpenLinkInWebBrowser.qml 1970-01-01 00:00:00 +0000
3+++ src/app/actions/OpenLinkInWebBrowser.qml 2015-12-18 18:01:09 +0000
4@@ -0,0 +1,23 @@
5+/*
6+ * Copyright 2015 Canonical Ltd.
7+ *
8+ * This file is part of webbrowser-app.
9+ *
10+ * webbrowser-app is free software; you can redistribute it and/or modify
11+ * it under the terms of the GNU General Public License as published by
12+ * the Free Software Foundation; version 3.
13+ *
14+ * webbrowser-app is distributed in the hope that it will be useful,
15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+ * GNU General Public License for more details.
18+ *
19+ * You should have received a copy of the GNU General Public License
20+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
21+ */
22+
23+import Ubuntu.Components 1.3
24+
25+Action {
26+ text: i18n.tr("Open link in WebBrowser")
27+}
28
29=== added file 'src/app/webcontainer/ContextMenuMobile.qml'
30--- src/app/webcontainer/ContextMenuMobile.qml 1970-01-01 00:00:00 +0000
31+++ src/app/webcontainer/ContextMenuMobile.qml 2015-12-18 18:01:09 +0000
32@@ -0,0 +1,157 @@
33+/*
34+ * Copyright 2015 Canonical Ltd.
35+ *
36+ * This file is part of webbrowser-app.
37+ *
38+ * webbrowser-app is free software; you can redistribute it and/or modify
39+ * it under the terms of the GNU General Public License as published by
40+ * the Free Software Foundation; version 3.
41+ *
42+ * webbrowser-app is distributed in the hope that it will be useful,
43+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
44+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45+ * GNU General Public License for more details.
46+ *
47+ * You should have received a copy of the GNU General Public License
48+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
49+ */
50+
51+import QtQuick 2.4
52+import Ubuntu.Components 1.3
53+import Ubuntu.Components.ListItems 1.3 as ListItems
54+import Ubuntu.Components.Popups 1.3 as Popups
55+import com.canonical.Oxide 1.8 as Oxide
56+
57+Popups.Dialog {
58+ property QtObject contextModel: model
59+ property ActionList actions: null
60+
61+ objectName: "contextMenuMobile"
62+
63+ QtObject {
64+ id: internal
65+ readonly property bool isImage: (contextModel.mediaType === Oxide.WebView.MediaTypeImage) ||
66+ (contextModel.mediaType === Oxide.WebView.MediaTypeCanvas)
67+ }
68+
69+ Row {
70+ id: header
71+ spacing: units.gu(2)
72+ anchors {
73+ left: parent.left
74+ leftMargin: units.gu(2)
75+ right: parent.right
76+ rightMargin: units.gu(2)
77+ }
78+ height: units.gu(2 * title.lineCount + 3)
79+ visible: title.text
80+
81+ Icon {
82+ width: units.gu(2)
83+ height: units.gu(2)
84+ anchors {
85+ top: parent.top
86+ topMargin: units.gu(2)
87+ }
88+ name: internal.isImage ? "stock_image" : ""
89+ // work around the lack of a standard stock_link symbolic icon in the theme
90+ Component.onCompleted: {
91+ if (!name) {
92+ source = "assets/stock_link.svg"
93+ }
94+ }
95+ }
96+
97+ Label {
98+ id: title
99+ objectName: "titleLabel"
100+ text: contextModel.srcUrl.toString() ? contextModel.srcUrl : contextModel.linkUrl
101+ width: parent.width - units.gu(4)
102+ anchors {
103+ top: parent.top
104+ topMargin: units.gu(2)
105+ bottom: parent.bottom
106+ }
107+ fontSize: "x-small"
108+ maximumLineCount: 2
109+ wrapMode: Text.Wrap
110+ height: contentHeight
111+ }
112+ }
113+
114+ ListItems.ThinDivider {
115+ anchors {
116+ left: parent.left
117+ leftMargin: units.gu(2)
118+ right: parent.right
119+ rightMargin: units.gu(2)
120+ }
121+ visible: header.visible
122+ }
123+
124+ Repeater {
125+ model: actions.actions
126+ delegate: ListItems.Empty {
127+ action: actions.actions[index]
128+ objectName: action.objectName + "_item"
129+ visible: action.enabled
130+ showDivider: false
131+
132+ height: units.gu(5)
133+
134+ Label {
135+ anchors {
136+ left: parent.left
137+ leftMargin: units.gu(2)
138+ right: parent.right
139+ rightMargin: units.gu(2)
140+ verticalCenter: parent.verticalCenter
141+ }
142+ fontSize: "x-small"
143+ text: action.text
144+ }
145+
146+ ListItems.ThinDivider {
147+ anchors {
148+ left: parent.left
149+ leftMargin: units.gu(2)
150+ right: parent.right
151+ rightMargin: units.gu(2)
152+ bottom: parent.bottom
153+ }
154+ }
155+
156+ onTriggered: contextModel.close()
157+ }
158+ }
159+
160+ ListItems.Empty {
161+ objectName: "cancelAction"
162+ height: units.gu(5)
163+ showDivider: false
164+ Label {
165+ anchors {
166+ left: parent.left
167+ leftMargin: units.gu(2)
168+ right: parent.right
169+ rightMargin: units.gu(2)
170+ verticalCenter: parent.verticalCenter
171+ }
172+ fontSize: "x-small"
173+ text: i18n.tr("Cancel")
174+ }
175+ onTriggered: contextModel.close()
176+ }
177+
178+ // adjust default dialog visuals to custom requirements for the context menu
179+ Binding {
180+ target: __foreground
181+ property: "margins"
182+ value: 0
183+ }
184+ Binding {
185+ target: __foreground
186+ property: "itemSpacing"
187+ value: 0
188+ }
189+}
190
191=== added file 'src/app/webcontainer/ContextMenuWide.qml'
192--- src/app/webcontainer/ContextMenuWide.qml 1970-01-01 00:00:00 +0000
193+++ src/app/webcontainer/ContextMenuWide.qml 2015-12-18 18:01:09 +0000
194@@ -0,0 +1,142 @@
195+/*
196+ * Copyright 2015 Canonical Ltd.
197+ *
198+ * This file is part of webbrowser-app.
199+ *
200+ * webbrowser-app is free software; you can redistribute it and/or modify
201+ * it under the terms of the GNU General Public License as published by
202+ * the Free Software Foundation; version 3.
203+ *
204+ * webbrowser-app is distributed in the hope that it will be useful,
205+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
206+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
207+ * GNU General Public License for more details.
208+ *
209+ * You should have received a copy of the GNU General Public License
210+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
211+ */
212+
213+import QtQuick 2.4
214+import Ubuntu.Components 1.3
215+import Ubuntu.Components.ListItems 1.3 as ListItems
216+import Ubuntu.Components.Popups 1.3 as Popups
217+import com.canonical.Oxide 1.8 as Oxide
218+
219+Popups.Popover {
220+ id: contextMenu
221+
222+ objectName: "contextMenuWide"
223+
224+ property QtObject contextModel: model
225+ property ActionList actions: null
226+ property var associatedWebview: null
227+
228+ QtObject {
229+ id: internal
230+
231+ readonly property int lastEnabledActionIndex: {
232+ var last = -1
233+ for (var i in actions.actions) {
234+ if (actions.actions[i].enabled) {
235+ last = i
236+ }
237+ }
238+ return last
239+ }
240+
241+ readonly property real locationBarOffset: contextMenu.associatedWebview.locationBarController.height
242+ + contextMenu.associatedWebview.locationBarController.offset
243+ }
244+
245+ Rectangle {
246+ anchors.fill: parent
247+ color: "#ececec"
248+ }
249+
250+ Column {
251+ anchors {
252+ left: parent.left
253+ right: parent.right
254+ }
255+
256+ Label {
257+ id: titleLabel
258+ objectName: "titleLabel"
259+ text: contextModel.srcUrl.toString() ? contextModel.srcUrl : contextModel.linkUrl
260+ anchors {
261+ left: parent.left
262+ leftMargin: units.gu(2)
263+ right: parent.right
264+ rightMargin: units.gu(2)
265+ }
266+ height: units.gu(5)
267+ visible: text
268+ fontSize: "x-small"
269+ color: "#888888"
270+ elide: Text.ElideRight
271+ verticalAlignment: Text.AlignVCenter
272+ }
273+
274+ ListItems.ThinDivider {
275+ anchors {
276+ left: parent.left
277+ leftMargin: units.gu(2)
278+ right: parent.right
279+ rightMargin: units.gu(2)
280+ }
281+ visible: titleLabel.visible
282+ }
283+
284+ Repeater {
285+ model: actions.actions
286+ delegate: ListItems.Empty {
287+ action: actions.actions[index]
288+ objectName: action.objectName + "_item"
289+ visible: action.enabled
290+ showDivider: false
291+
292+ height: units.gu(5)
293+
294+ Label {
295+ anchors {
296+ left: parent.left
297+ leftMargin: units.gu(2)
298+ right: parent.right
299+ rightMargin: units.gu(2)
300+ verticalCenter: parent.verticalCenter
301+ }
302+ fontSize: "small"
303+ text: action.text
304+ }
305+
306+ ListItems.ThinDivider {
307+ visible: index < internal.lastEnabledActionIndex
308+ anchors {
309+ left: parent.left
310+ leftMargin: units.gu(2)
311+ right: parent.right
312+ rightMargin: units.gu(2)
313+ bottom: parent.bottom
314+ }
315+ }
316+
317+ onTriggered: contextMenu.hide()
318+ }
319+ }
320+ }
321+
322+ Item {
323+ id: positioner
324+ visible: false
325+ parent: contextMenu.associatedWebview
326+ x: contextModel.position.x
327+ y: contextModel.position.y + internal.locationBarOffset
328+ }
329+ caller: positioner
330+
331+ onVisibleChanged: {
332+ if (!visible) {
333+ contextModel.close()
334+ }
335+ }
336+}
337
338=== modified file 'src/app/webcontainer/WebApp.qml'
339--- src/app/webcontainer/WebApp.qml 2015-09-29 17:02:25 +0000
340+++ src/app/webcontainer/WebApp.qml 2015-12-18 18:01:09 +0000
341@@ -124,6 +124,7 @@
342 id: containerWebView
343 objectName: "webview"
344
345+ wide: webapp.wide
346 anchors {
347 left: parent.left
348 right: parent.right
349
350=== modified file 'src/app/webcontainer/WebViewImplOxide.qml'
351--- src/app/webcontainer/WebViewImplOxide.qml 2015-08-20 10:42:09 +0000
352+++ src/app/webcontainer/WebViewImplOxide.qml 2015-12-18 18:01:09 +0000
353@@ -37,6 +37,7 @@
354 property url dataPath
355 property var popupController
356 property var overlayViewsParent: webview.parent
357+ property bool wide: false
358
359 // Mostly used for testing & avoid external urls to
360 // "leak" in the default browser. External URLs corresponds
361@@ -66,53 +67,106 @@
362
363 onNewViewRequested: popupController.createPopupView(overlayViewsParent, request, true, context)
364
365+ property QtObject contextModel: null
366 contextualActions: ActionList {
367+ Actions.OpenLinkInWebBrowser {
368+ objectName: "OpenLinkInWebBrowser"
369+ enabled: contextModel && contextModel.linkUrl.toString()
370+ onTriggered: openUrlExternally(contextModel.linkUrl)
371+ }
372 Actions.CopyLink {
373 enabled: webview.contextModel && webview.contextModel.linkUrl.toString()
374 onTriggered: Clipboard.push(["text/plain", contextModel.linkUrl.toString()])
375+ objectName: "CopyLinkContextualAction"
376 }
377 Actions.CopyImage {
378 enabled: webview.contextModel &&
379 (webview.contextModel.mediaType === Oxide.WebView.MediaTypeImage) &&
380 webview.contextModel.srcUrl.toString()
381 onTriggered: Clipboard.push(["text/plain", contextModel.srcUrl.toString()])
382+ objectName: "CopyImageContextualAction"
383 }
384 Actions.Undo {
385 enabled: webview.contextModel && webview.contextModel.isEditable &&
386 (webview.contextModel.editFlags & Oxide.WebView.UndoCapability)
387 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandUndo)
388+ objectName: "UndoContextualAction"
389 }
390 Actions.Redo {
391 enabled: webview.contextModel && webview.contextModel.isEditable &&
392 (webview.contextModel.editFlags & Oxide.WebView.RedoCapability)
393 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandRedo)
394+ objectName: "RedoContextualAction"
395 }
396 Actions.Cut {
397 enabled: webview.contextModel && webview.contextModel.isEditable &&
398 (webview.contextModel.editFlags & Oxide.WebView.CutCapability)
399 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandCut)
400+ objectName: "CutContextualAction"
401 }
402 Actions.Copy {
403 enabled: webview.contextModel && webview.contextModel.isEditable &&
404 (webview.contextModel.editFlags & Oxide.WebView.CopyCapability)
405 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandCopy)
406+ objectName: "CopyContextualAction"
407 }
408 Actions.Paste {
409 enabled: webview.contextModel && webview.contextModel.isEditable &&
410 (webview.contextModel.editFlags & Oxide.WebView.PasteCapability)
411 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandPaste)
412+ objectName: "PasteContextualAction"
413 }
414 Actions.Erase {
415 enabled: webview.contextModel && webview.contextModel.isEditable &&
416 (webview.contextModel.editFlags & Oxide.WebView.EraseCapability)
417 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandErase)
418+ objectName: "EraseContextualAction"
419 }
420 Actions.SelectAll {
421 enabled: webview.contextModel && webview.contextModel.isEditable &&
422 (webview.contextModel.editFlags & Oxide.WebView.SelectAllCapability)
423 onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandSelectAll)
424- }
425- }
426+ objectName: "SelectAllContextualAction"
427+ }
428+ }
429+ function contextMenuOnCompleted(menu) {
430+ if (!menu || !menu.contextModel) {
431+ return
432+ }
433+ contextModel = menu.contextModel
434+
435+ var isImageMediaType =
436+ ((contextModel.mediaType === Oxide.WebView.MediaTypeImage) ||
437+ (contextModel.mediaType === Oxide.WebView.MediaTypeCanvas))
438+ && contextModel.hasImageContents;
439+
440+ if (contextModel.linkUrl.toString() ||
441+ contextModel.srcUrl.toString() ||
442+ contextModel.selectionText ||
443+ (contextModel.isEditable && contextModel.editFlags) ||
444+ isImageMediaType) {
445+ menu.show()
446+ } else {
447+ contextModel.close()
448+ }
449+ }
450+ Component {
451+ id: contextMenuNarrowComponent
452+ ContextMenuMobile {
453+ actions: contextualActions
454+ Component.onCompleted: webview.contextMenuOnCompleted(this)
455+ }
456+ }
457+ Component {
458+ id: contextMenuWideComponent
459+ ContextMenuWide {
460+ associatedWebview: webview
461+ parent: browser
462+ actions: contextualActions
463+ Component.onCompleted: webview.contextMenuOnCompleted(this)
464+ }
465+ }
466+ contextMenu: webview.wide ? contextMenuWideComponent : contextMenuNarrowComponent
467
468 StateSaver.properties: "url"
469 StateSaver.enabled: !runningLocalApplication
470
471=== modified file 'src/app/webcontainer/WebappContainerWebview.qml'
472--- src/app/webcontainer/WebappContainerWebview.qml 2015-08-10 15:22:00 +0000
473+++ src/app/webcontainer/WebappContainerWebview.qml 2015-12-18 18:01:09 +0000
474@@ -39,9 +39,12 @@
475 property url webviewOverrideFile: ""
476 property bool blockOpenExternalUrls: false
477 property bool runningLocalApplication: false
478+ property bool wide: false
479
480 signal samlRequestUrlPatternReceived(string urlPattern)
481
482+ onWideChanged: if (webappContainerWebViewLoader.item) webappContainerWebViewLoader.item.wide = wide
483+
484 PopupWindowController {
485 id: popupController
486 objectName: "popupController"
487@@ -87,7 +90,8 @@
488 , blockOpenExternalUrls: containerWebview.blockOpenExternalUrls
489 , runningLocalApplication: containerWebview.runningLocalApplication
490 , popupController: popupController
491- , overlayViewsParent: containerWebview.parent})
492+ , overlayViewsParent: containerWebview.parent
493+ , wide: containerWebview.wide})
494 }
495 }
496
497
498=== added file 'src/app/webcontainer/assets/stock_link.svg'
499--- src/app/webcontainer/assets/stock_link.svg 1970-01-01 00:00:00 +0000
500+++ src/app/webcontainer/assets/stock_link.svg 2015-12-18 18:01:09 +0000
501@@ -0,0 +1,164 @@
502+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
503+<!-- Created with Inkscape (http://www.inkscape.org/) -->
504+
505+<svg
506+ xmlns:dc="http://purl.org/dc/elements/1.1/"
507+ xmlns:cc="http://creativecommons.org/ns#"
508+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
509+ xmlns:svg="http://www.w3.org/2000/svg"
510+ xmlns="http://www.w3.org/2000/svg"
511+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
512+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
513+ width="90"
514+ height="90"
515+ id="svg4874"
516+ version="1.1"
517+ inkscape:version="0.48+devel r"
518+ viewBox="0 0 90 90.000001"
519+ sodipodi:docname="insert-link02b.svg">
520+ <defs
521+ id="defs4876" />
522+ <sodipodi:namedview
523+ id="base"
524+ pagecolor="#ffffff"
525+ bordercolor="#666666"
526+ borderopacity="1.0"
527+ inkscape:pageopacity="0.0"
528+ inkscape:pageshadow="2"
529+ inkscape:zoom="5.0931704"
530+ inkscape:cx="51.019301"
531+ inkscape:cy="39.641321"
532+ inkscape:document-units="px"
533+ inkscape:current-layer="g4978"
534+ showgrid="true"
535+ showborder="true"
536+ fit-margin-top="0"
537+ fit-margin-left="0"
538+ fit-margin-right="0"
539+ fit-margin-bottom="0"
540+ inkscape:snap-bbox="true"
541+ inkscape:bbox-paths="true"
542+ inkscape:bbox-nodes="true"
543+ inkscape:snap-bbox-edge-midpoints="true"
544+ inkscape:snap-bbox-midpoints="true"
545+ inkscape:object-paths="true"
546+ inkscape:snap-intersection-paths="true"
547+ inkscape:object-nodes="true"
548+ inkscape:snap-smooth-nodes="true"
549+ inkscape:snap-midpoints="true"
550+ inkscape:snap-object-midpoints="true"
551+ inkscape:snap-center="true"
552+ showguides="true"
553+ inkscape:guide-bbox="true">
554+ <inkscape:grid
555+ type="xygrid"
556+ id="grid5451"
557+ empspacing="6" />
558+ <sodipodi:guide
559+ orientation="1,0"
560+ position="6,77"
561+ id="guide4063" />
562+ <sodipodi:guide
563+ orientation="1,0"
564+ position="3,78"
565+ id="guide4065" />
566+ <sodipodi:guide
567+ orientation="0,1"
568+ position="55,84"
569+ id="guide4067" />
570+ <sodipodi:guide
571+ orientation="0,1"
572+ position="53,87"
573+ id="guide4069" />
574+ <sodipodi:guide
575+ orientation="0,1"
576+ position="20,3"
577+ id="guide4071" />
578+ <sodipodi:guide
579+ orientation="0,1"
580+ position="20,6"
581+ id="guide4073" />
582+ <sodipodi:guide
583+ orientation="1,0"
584+ position="87,7"
585+ id="guide4075" />
586+ <sodipodi:guide
587+ orientation="1,0"
588+ position="84,7"
589+ id="guide4077" />
590+ <sodipodi:guide
591+ orientation="0,1"
592+ position="58,81"
593+ id="guide4074" />
594+ <sodipodi:guide
595+ orientation="1,0"
596+ position="9,74"
597+ id="guide4076" />
598+ <sodipodi:guide
599+ orientation="0,1"
600+ position="21,9"
601+ id="guide4078" />
602+ <sodipodi:guide
603+ orientation="1,0"
604+ position="81,4"
605+ id="guide4080" />
606+ </sodipodi:namedview>
607+ <metadata
608+ id="metadata4879">
609+ <rdf:RDF>
610+ <cc:Work
611+ rdf:about="">
612+ <dc:format>image/svg+xml</dc:format>
613+ <dc:type
614+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
615+ <dc:title></dc:title>
616+ </cc:Work>
617+ </rdf:RDF>
618+ </metadata>
619+ <g
620+ inkscape:label="Layer 1"
621+ inkscape:groupmode="layer"
622+ id="layer1"
623+ transform="translate(67.857146,-84.50504)">
624+ <g
625+ transform="matrix(0,-1,-1,0,373.50506,516.50504)"
626+ id="g4845"
627+ style="display:inline">
628+ <g
629+ inkscape:label="Layer 1"
630+ id="layer1-8"
631+ transform="matrix(0,-1,-1,0,1394.3622,441.36221)">
632+ <rect
633+ style="color:#000000;fill:none;stroke:none;stroke-width:7.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
634+ id="rect4198"
635+ width="90"
636+ height="90"
637+ x="0"
638+ y="962.36218" />
639+ <g
640+ id="g4978"
641+ transform="translate(-60,548.00002)">
642+ <rect
643+ style="color:#000000;fill:none;stroke:none;stroke-width:6;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
644+ id="rect3038"
645+ width="90"
646+ height="90"
647+ x="0"
648+ y="1.7382814e-05"
649+ transform="translate(60,414.36216)" />
650+ <path
651+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#808080;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
652+ d="M 92.075086,472.28711 117.92492,446.43727"
653+ id="path4188"
654+ inkscape:connector-curvature="0"
655+ sodipodi:nodetypes="cc" />
656+ <path
657+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
658+ d="M 63.667969 9.0371094 C 63.165875 9.0539444 62.671019 9.1114844 62.183594 9.2089844 C 58.284224 9.9890744 55.804541 12.722793 53.650391 14.876953 L 47.1875 21.339844 C 45.03334 23.493994 42.301574 25.973667 41.521484 29.873047 C 41.188085 31.539603 41.327146 33.294372 41.955078 35.121094 L 49.910156 27.164062 C 50.372184 26.657905 50.880803 26.130913 51.429688 25.582031 L 57.892578 19.119141 C 60.046728 16.964991 61.875558 15.389037 63.361328 15.091797 C 63.547048 15.054647 63.742359 15.027806 63.949219 15.017578 C 64.156079 15.007351 64.374492 15.013756 64.607422 15.041016 C 66.005022 15.204646 67.917154 16.153481 70.882812 19.119141 C 74.837032 23.073351 75.205443 25.154865 74.908203 26.640625 C 74.610973 28.126395 73.035009 29.953262 70.880859 32.107422 L 64.419922 38.570312 C 63.865878 39.124359 63.33492 39.635932 62.824219 40.101562 L 54.878906 48.046875 C 56.706268 48.675332 58.461808 48.813981 60.128906 48.480469 C 64.028276 47.700379 66.507959 44.96665 68.662109 42.8125 L 75.125 36.349609 C 77.27915 34.195449 80.010926 31.715786 80.791016 27.816406 C 81.571096 23.917036 79.78641 19.5364 75.125 14.875 C 71.62895 11.37895 68.290852 9.500825 65.195312 9.109375 C 64.679394 9.044135 64.170062 9.0202744 63.667969 9.0371094 z M 31.355469 41.349609 C 30.853375 41.366444 30.358515 41.423984 29.871094 41.521484 C 25.971725 42.301574 23.492043 45.035303 21.337891 47.189453 L 14.875 53.650391 C 12.720847 55.804541 9.9890734 58.286177 9.2089844 62.185547 C 8.4288954 66.084907 10.213595 70.465553 14.875 75.126953 C 19.536407 79.788363 23.917035 81.573059 27.816406 80.792969 C 31.715777 80.012879 34.195456 77.27915 36.349609 75.125 L 42.8125 68.662109 C 44.96665 66.507949 47.698426 64.028286 48.478516 60.128906 C 48.812028 58.461808 48.673379 56.706268 48.044922 54.878906 L 40.109375 62.814453 C 39.641569 63.32819 39.12796 63.862274 38.570312 64.419922 L 32.107422 70.882812 C 29.953269 73.036972 28.124437 74.610973 26.638672 74.908203 C 25.152907 75.205443 23.071405 74.837002 19.117188 70.882812 C 15.162971 66.928594 14.794562 64.847088 15.091797 63.361328 C 15.389032 61.875558 16.964989 60.048681 19.119141 57.894531 L 25.580078 51.431641 C 26.132543 50.879177 26.662506 50.368883 27.171875 49.904297 L 35.119141 41.957031 C 34.361083 41.696439 33.61486 41.514447 32.882812 41.421875 C 32.366891 41.356635 31.857562 41.332774 31.355469 41.349609 z "
659+ transform="translate(60.000003,414.36218)"
660+ id="path4190" />
661+ </g>
662+ </g>
663+ </g>
664+ </g>
665+</svg>
666
667=== modified file 'tests/autopilot/webapp_container/tests/fake_servers.py'
668--- tests/autopilot/webapp_container/tests/fake_servers.py 2015-05-27 16:19:45 +0000
669+++ tests/autopilot/webapp_container/tests/fake_servers.py 2015-12-18 18:01:09 +0000
670@@ -138,6 +138,11 @@
671 </html>
672 """
673
674+ base64_png_data = \
675+ "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAACXBIWXMAAAsTAAALEwE" \
676+ "AmpwYAAAAOUlEQVRYw+3OAQ0AAAgDoGv/zlpDN0hATS7qaGlpaWlpaWlpaWlpaWlpaW" \
677+ "lpaWlpaWlpaWlpab1qLUGqAWNyFWTYAAAAAElFTkSuQmCC"
678+
679 def do_GET(self):
680 if self.path == '/':
681 self.send_response(200)
682@@ -151,6 +156,28 @@
683 elif self.path == '/with-external-link':
684 self.send_response(200)
685 self.serve_content(self.external_click_content())
686+ elif self.path == "/image":
687+ self.send_response(200)
688+ html = '<html><body>'
689+ html += '<img src="data:image/png;base64,' + self.base64_png_data
690+ html += '" style="position: fixed; top: 50%; left: 50%; '
691+ html += 'transform: translate(-50%, -50%)" />'
692+ html += '</body></html>'
693+ self.serve_content(html)
694+ elif self.path == "/imagelink":
695+ self.send_response(200)
696+ html = '<html><body><a href="http://www.ubuntu.com">'
697+ html += '<img src="data:image/png;base64,' + self.base64_png_data
698+ html += '" style="position: fixed; top: 50%; left: 50%; '
699+ html += 'transform: translate(-50%, -50%)" />'
700+ html += '</a></body></html>'
701+ self.serve_content(html)
702+ elif self.path == "/textarea":
703+ self.send_response(200)
704+ html = '<html><body style="margin: 0">'
705+ html += '<textarea style="width: 100%; height: 100%">some text'
706+ html += '</textarea></body></html>'
707+ self.serve_content(html)
708 elif self.path == '/with-targetted-link':
709 self.send_response(200)
710 self.serve_content(self.targetted_click_content())
711
712=== added file 'tests/autopilot/webapp_container/tests/test_context_menu.py'
713--- tests/autopilot/webapp_container/tests/test_context_menu.py 1970-01-01 00:00:00 +0000
714+++ tests/autopilot/webapp_container/tests/test_context_menu.py 2015-12-18 18:01:09 +0000
715@@ -0,0 +1,189 @@
716+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
717+#
718+# Copyright 2015 Canonical
719+#
720+# This program is free software: you can redistribute it and/or modify it
721+# under the terms of the GNU General Public License version 3, as published
722+# by the Free Software Foundation.
723+#
724+# This program is distributed in the hope that it will be useful,
725+# but WITHOUT ANY WARRANTY; without even the implied warranty of
726+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
727+# GNU General Public License for more details.
728+#
729+# You should have received a copy of the GNU General Public License
730+# along with this program. If not, see <http://www.gnu.org/licenses/>.
731+
732+import time
733+
734+from autopilot.platform import model
735+from autopilot.matchers import Eventually
736+from testtools.matchers import Equals, StartsWith
737+
738+from webapp_container.tests import WebappContainerTestCaseWithLocalContentBase
739+
740+import ubuntuuitoolkit as uitk
741+
742+
743+class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase):
744+ def get_title_label(self):
745+ return self.select_single(objectName="titleLabel")
746+
747+ def get_visible_actions(self):
748+ return self.select_many("Empty", visible=True)
749+
750+ def get_action(self, objectName):
751+ name = objectName + "_item"
752+ return self.select_single("Empty", objectName=name)
753+
754+ def click_action(self, objectName):
755+ name = objectName + "_item"
756+ action = self.select_single("Empty", visible=True,
757+ enabled=True, objectName=name)
758+ self.pointing_device.click_object(action)
759+ self.wait_until_destroyed()
760+
761+
762+class ContextMenuWide(ContextMenuBase):
763+ pass
764+
765+
766+class ContextMenuMobile(ContextMenuBase):
767+ def click_cancel_action(self):
768+ action = self.select_single("Empty", objectName="cancelAction")
769+ self.pointing_device.click_object(action)
770+
771+
772+class TestContextMenuBase(WebappContainerTestCaseWithLocalContentBase):
773+ data_uri_prefix = "data:image/png;base64,"
774+
775+ def _get_context_menu(self):
776+ if self.get_webcontainer_webview().wide:
777+ return self.app.wait_select_single(
778+ ContextMenuWide,
779+ objectName="contextMenuWide")
780+ else:
781+ return self.app.wait_select_single(
782+ ContextMenuMobile,
783+ objectName="contextMenuMobile")
784+
785+ def _open_context_menu(self):
786+ webview = self.get_webview()
787+ x = webview.globalRect.x + webview.globalRect.width // 2
788+ y = webview.globalRect.y + webview.globalRect.height // 2
789+ self.pointing_device.move(x, y)
790+ if model() == 'Desktop':
791+ self.pointing_device.click(button=3)
792+ else:
793+ self.pointing_device.press()
794+ time.sleep(1.5)
795+ self.pointing_device.release()
796+ return self._get_context_menu()
797+
798+ def _dismiss_context_menu(self, menu):
799+ if self.get_webcontainer_webview().wide:
800+ # Dismiss by clicking outside of the menu
801+ webview_rect = self.get_webview().globalRect
802+ actions = menu.get_visible_actions()
803+ outside_x = (webview_rect.x + actions[0].globalRect.x) // 2
804+ outside_y = webview_rect.y + webview_rect.height // 2
805+ self.pointing_device.move(outside_x, outside_y)
806+ self.pointing_device.click()
807+ else:
808+ # Dismiss by clicking the cancel action
809+ menu.click_cancel_action()
810+ menu.wait_until_destroyed()
811+
812+ def setUp(self, path):
813+ super(TestContextMenuBase, self).setUp()
814+ args = []
815+ self.launch_webcontainer_app_with_local_http_server(
816+ args,
817+ path,
818+ {'WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY': '1'})
819+ self.get_webcontainer_window().visible.wait_for(True)
820+ self.menu = self._open_context_menu()
821+
822+
823+class TestContextMenuLink(TestContextMenuBase):
824+
825+ def setUp(self):
826+ super(TestContextMenuLink, self).setUp(path="/with-external-link")
827+ self.assertThat(self.menu.get_title_label().text,
828+ Equals("http://www.ubuntu.com/"))
829+
830+ def test_open_link_(self):
831+ main_webview = self.get_oxide_webview()
832+ signal = main_webview.watch_signal(
833+ 'openExternalUrlTriggered(QString)')
834+ self.assertThat(signal.was_emitted, Equals(False))
835+
836+ self.menu.click_action("OpenLinkInWebBrowser")
837+
838+ self.assertThat(lambda: signal.was_emitted, Eventually(Equals(True)))
839+ self.assertThat(signal.num_emissions, Equals(1))
840+
841+ def test_copy_link(self):
842+ self.menu.click_action("CopyLinkContextualAction")
843+
844+
845+class TestContextMenuImage(TestContextMenuBase):
846+
847+ def setUp(self):
848+ super(TestContextMenuImage, self).setUp(path="/image")
849+ self.assertThat(self.menu.get_title_label().text,
850+ StartsWith(self.data_uri_prefix))
851+
852+ def test_copy_image(self):
853+ # There is no easy way to test the contents of the clipboard,
854+ # but we can at least verify that the context menu was dismissed.
855+ self.menu.click_action("CopyImageContextualAction")
856+
857+
858+class TestContextMenuImageAndLink(TestContextMenuBase):
859+
860+ def setUp(self):
861+ super(TestContextMenuImageAndLink, self).setUp(path="/imagelink")
862+ self.assertThat(self.menu.get_title_label().text,
863+ StartsWith(self.data_uri_prefix))
864+
865+ def test_open_link_in_webbrowser(self):
866+ main_webview = self.get_oxide_webview()
867+ signal = main_webview.watch_signal(
868+ 'openExternalUrlTriggered(QString)')
869+ self.assertThat(signal.was_emitted, Equals(False))
870+
871+ self.menu.click_action("OpenLinkInWebBrowser")
872+
873+ self.assertThat(lambda: signal.was_emitted, Eventually(Equals(True)))
874+ self.assertThat(signal.num_emissions, Equals(1))
875+
876+ def test_copy_link(self):
877+ # There is no easy way to test the contents of the clipboard,
878+ # but we can at least verify that the context menu was dismissed.
879+ self.menu.click_action("CopyLinkContextualAction")
880+
881+ def test_copy_image(self):
882+ # There is no easy way to test the contents of the clipboard,
883+ # but we can at least verify that the context menu was dismissed.
884+ self.menu.click_action("CopyImageContextualAction")
885+
886+
887+class TestContextMenuTextArea(TestContextMenuBase):
888+
889+ def setUp(self):
890+ super(TestContextMenuTextArea, self).setUp(path="/textarea")
891+ self.assertThat(self.menu.get_title_label().visible, Equals(False))
892+
893+ def test_actions(self):
894+ actions = ["SelectAll",
895+ "Cut",
896+ "Undo",
897+ "Redo",
898+ "Paste",
899+ "SelectAll",
900+ "Copy",
901+ "Erase"]
902+ for action in actions:
903+ self.menu.click_action("{}ContextualAction".format(action))
904+ self.menu = self._open_context_menu()

Subscribers

People subscribed via source and target branches

to status/vote changes: