Merge lp:~osomon/webbrowser-app/oxide-context-menu into lp:webbrowser-app
- oxide-context-menu
- Merge into trunk
Status: | Merged | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Olivier Tilloy | ||||||||||||||||||||||||||||||||||||||||
Approved revision: | 1142 | ||||||||||||||||||||||||||||||||||||||||
Merged at revision: | 1152 | ||||||||||||||||||||||||||||||||||||||||
Proposed branch: | lp:~osomon/webbrowser-app/oxide-context-menu | ||||||||||||||||||||||||||||||||||||||||
Merge into: | lp:webbrowser-app | ||||||||||||||||||||||||||||||||||||||||
Diff against target: |
2579 lines (+1390/-690) 34 files modified
debian/control (+2/-12) debian/qtdeclarative5-ubuntu-web-plugin-assets.install (+0/-1) debian/rules (+0/-4) doc/WebView.qdoc (+44/-5) src/Ubuntu/Web/CMakeLists.txt (+0/-3) src/Ubuntu/Web/Selection.qml (+0/-161) src/Ubuntu/Web/SelectionHandle.qml (+0/-47) src/Ubuntu/Web/UbuntuWebView02.qml (+60/-156) src/Ubuntu/Web/selection02.js (+2/-131) src/app/CMakeLists.txt (+1/-0) src/app/FileExtensionMapper.js (+11/-7) src/app/FilePickerDialog.qml (+2/-2) src/app/WebViewImpl.qml (+20/-9) src/app/actions/Cut.qml (+23/-0) src/app/actions/Erase.qml (+23/-0) src/app/actions/Paste.qml (+23/-0) src/app/actions/Redo.qml (+23/-0) src/app/actions/SaveLink.qml (+23/-0) src/app/actions/SelectAll.qml (+23/-0) src/app/actions/Undo.qml (+23/-0) src/app/browserapplication.cpp (+9/-0) src/app/mime-database.cpp (+33/-0) src/app/mime-database.h (+39/-0) src/app/webbrowser/Browser.qml (+103/-26) src/app/webbrowser/ContextMenuMobile.qml (+168/-0) src/app/webbrowser/ContextMenuWide.qml (+158/-0) src/app/webbrowser/assets/stock_link.svg (+164/-0) src/app/webcontainer/WebViewImplOxide.qml (+42/-5) tests/autopilot/webbrowser_app/emulators/browser.py (+33/-16) tests/autopilot/webbrowser_app/tests/http_server.py (+28/-0) tests/autopilot/webbrowser_app/tests/test_contextmenu.py (+177/-0) tests/autopilot/webbrowser_app/tests/test_selection.py (+0/-92) tests/unittests/qml/tst_FileExtensionMapper.qml (+39/-0) tests/unittests/qml/tst_UbuntuWebView02.qml (+94/-13) |
||||||||||||||||||||||||||||||||||||||||
To merge this branch: | bzr merge lp:~osomon/webbrowser-app/oxide-context-menu | ||||||||||||||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
Ugo Riboni (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+268786@code.launchpad.net |
Commit message
Use the contextMenu API new in oxide 1.8.
Update the visuals for the context menu in narrow and wide form factors.
Add text editing commands to the context menu.
Add unit and autopilot tests for the context menu features.
This bumps the runtime dependency of webapp-container and qtdeclarative5-
This also removes the qtdeclarative5-
Description of the change
Use the contextMenu API new in oxide 1.8.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1129
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1130
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://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1133
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://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1135
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://
Ugo Riboni (uriboni) wrote : | # |
Works mostly as expected on desktop. Have not tested on device yet or ran tests myself.
I included various comments inline based on review of the code.
I am not sure if revision 1133 belongs in this MR, even though I understand why it was easy to include it. Up to you if you want to consider splitting it off, but it would certainly seem cleaner from a repository cleanliness point of view.
Olivier Tilloy (osomon) wrote : | # |
Thanks for your thorough review. I replied all your comments inline (and updated the code where relevant). The only pending task is to update the code to use QMimeDatabase per your recommendation, which I’m working on now.
I removed the selection mechanism as part of this MR because in fact this new context menu implementation broke it even further (it had already been quite broken since its initial implementation, but this made things slightly worse, and I spent way too much time trying to fix it). In that regard it made sense to remove it now.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1138
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://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1139
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1139
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://
Ugo Riboni (uriboni) wrote : | # |
LGTM now and it all seems to work as expected. Tests are all ok too.
Ken VanDine (ken-vandine) wrote : | # |
Packaging changes are fine
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2015-08-16 23:11:48 +0000 |
3 | +++ debian/control 2015-08-27 14:01:06 +0000 |
4 | @@ -60,7 +60,7 @@ |
5 | Depends: ${misc:Depends}, |
6 | ${shlibs:Depends}, |
7 | fonts-liberation, |
8 | - liboxideqt-qmlplugin (>= 1.5), |
9 | + liboxideqt-qmlplugin (>= 1.8), |
10 | libqt5sql5-sqlite, |
11 | qml-module-qtquick2 (>= 5.4), |
12 | qml-module-qtquick-window2 (>= 5.3), |
13 | @@ -97,24 +97,14 @@ |
14 | Pre-Depends: ${misc:Pre-Depends} |
15 | Depends: ${misc:Depends}, |
16 | ${shlibs:Depends}, |
17 | - liboxideqt-qmlplugin (>= 1.6), |
18 | + liboxideqt-qmlplugin (>= 1.8), |
19 | qml-module-qtquick2 (>= 5.4), |
20 | qml-module-qtquick-window2 (>= 5.3), |
21 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3), |
22 | - qtdeclarative5-ubuntu-web-plugin-assets (>= ${source:Version}), |
23 | Description: Ubuntu web QML plugin |
24 | A standalone QML plugin that contains the WebView component, |
25 | in the Ubuntu.Web module. |
26 | |
27 | -Package: qtdeclarative5-ubuntu-web-plugin-assets |
28 | -Architecture: all |
29 | -Multi-Arch: foreign |
30 | -Depends: ${misc:Depends}, |
31 | -Description: Ubuntu web QML plugin assets |
32 | - A standalone QML plugin that contains the WebView component, |
33 | - in the Ubuntu.Web module. This package contains the PNGs used |
34 | - as UI elements by the plugin. |
35 | - |
36 | Package: qtdeclarative5-ubuntu-web-plugin-doc |
37 | Section: doc |
38 | Architecture: all |
39 | |
40 | === removed file 'debian/qtdeclarative5-ubuntu-web-plugin-assets.install' |
41 | --- debian/qtdeclarative5-ubuntu-web-plugin-assets.install 2014-06-19 08:26:10 +0000 |
42 | +++ debian/qtdeclarative5-ubuntu-web-plugin-assets.install 1970-01-01 00:00:00 +0000 |
43 | @@ -1,1 +0,0 @@ |
44 | -usr/share/qtdeclarative5-ubuntu-web-plugin/ |
45 | |
46 | === modified file 'debian/rules' |
47 | --- debian/rules 2015-02-26 18:10:41 +0000 |
48 | +++ debian/rules 2015-08-27 14:01:06 +0000 |
49 | @@ -11,10 +11,6 @@ |
50 | dh $@ --parallel --with translations |
51 | |
52 | override_dh_install: |
53 | - ln -sf /usr/share/qtdeclarative5-ubuntu-web-plugin/assets \ |
54 | - $(CURDIR)/debian/tmp/usr/lib/*/qt5/qml/Ubuntu/Web |
55 | - ln -sf /usr/share/qtdeclarative5-ubuntu-web-plugin/assets \ |
56 | - $(CURDIR)/debian/tmp/usr/lib/*/qt5/qml/Ubuntu/Components/Extras/Browser |
57 | dh_install --fail-missing |
58 | |
59 | override_dh_translations: |
60 | |
61 | === modified file 'doc/WebView.qdoc' |
62 | --- doc/WebView.qdoc 2015-08-10 15:22:00 +0000 |
63 | +++ doc/WebView.qdoc 2015-08-27 14:01:06 +0000 |
64 | @@ -172,30 +172,33 @@ |
65 | context menu (by way of a right click on desktop, or a long press on a |
66 | touch-enabled device, on an image or a hyperlink). |
67 | By default the list is empty, and no menu is shown. |
68 | - User-defined actions can access the \l {contextualData} {contextual data}. |
69 | + User-defined actions can access the \l {contextModel} {context model}. |
70 | |
71 | Example of user-defined actions: |
72 | \code |
73 | - import Ubuntu.Components 1.1 |
74 | + import Ubuntu.Components 1.3 |
75 | import Ubuntu.Web 0.2 |
76 | |
77 | WebView { |
78 | contextualActions: ActionList { |
79 | Action { |
80 | text: i18n.tr("Open link in browser") |
81 | - enabled: contextualData.href.toString() |
82 | - onTriggered: Qt.openUrlExternally(contextualData.href) |
83 | + enabled: contextModel && contextModel.linkUrl.toString() |
84 | + onTriggered: Qt.openUrlExternally(contextModel.linkUrl) |
85 | } |
86 | } |
87 | } |
88 | \endcode |
89 | |
90 | - \sa contextualData |
91 | + \sa contextModel |
92 | */ |
93 | |
94 | /*! |
95 | + \deprecated |
96 | \qmlproperty QtObject WebView::contextualData |
97 | |
98 | + This property is deprecated, use the \l {contextModel} property instead. |
99 | + |
100 | An object that holds the contextual data associated with the current context |
101 | menu. User-defined \l {contextualActions} {contextual actions} can use this |
102 | data to process it when triggered. |
103 | @@ -211,6 +214,42 @@ |
104 | and \c img will be available, allowing a user-defined contextual action to |
105 | operate on both elements. |
106 | |
107 | + \sa contextualActions, contextModel |
108 | + */ |
109 | + |
110 | +/*! |
111 | + \qmlproperty QtObject WebView::contextModel |
112 | + |
113 | + An object that holds the contextual data associated with the current context |
114 | + menu, as well as methods to interact with this data. User-defined |
115 | + \l {contextualActions} {contextual actions} can use this data to process it |
116 | + when triggered. |
117 | + |
118 | + It has the following properties: |
119 | + \list |
120 | + \li linkUrl (url): the full URI of the hyperlink, if any |
121 | + \li srcUrl (url): the full URI of the image/media, if any |
122 | + \li mediaType (int): the type of media (one of Oxide.WebView.MediaTypeNone, |
123 | + Oxide.WebView.MediaTypeImage, Oxide.WebView.MediaTypeCanvas, |
124 | + Oxide.WebView.MediaTypeAudio, Oxide.WebView.MediaTypeVideo) |
125 | + \li isEditable (bool): whether the current element is editable |
126 | + \li editFlags (int): for editable elements, an OR-combined list of flags that |
127 | + define the current editing capabilities (Oxide.WebView.UndoCapability, |
128 | + Oxide.WebView.RedoCapability, Oxide.WebView.CutCapability, |
129 | + Oxide.WebView.CopyCapability, Oxide.WebView.PasteCapability, |
130 | + Oxide.WebView.EraseCapability, Oxide.WebView.SelectAllCapability) |
131 | + \endlist |
132 | + |
133 | + It has the following methods: |
134 | + \list |
135 | + \li saveLink(): initiates a download request for the resource pointed |
136 | + to by the hyperlink, if any |
137 | + \li saveMedia(): initiates a download request for the media |
138 | + (image, canvas, audio, video), if any |
139 | + \endlist |
140 | + |
141 | + When there is no active context menu, \c contextModel is null. |
142 | + |
143 | \sa contextualActions |
144 | */ |
145 | |
146 | |
147 | === removed symlink 'src/Ubuntu/Components/Extras/Browser/Selection.qml' |
148 | === target was u'../../../Web/Selection.qml' |
149 | === removed symlink 'src/Ubuntu/Components/Extras/Browser/SelectionHandle.qml' |
150 | === target was u'../../../Web/SelectionHandle.qml' |
151 | === removed symlink 'src/Ubuntu/Components/Extras/Browser/assets' |
152 | === target was u'../../../Web/assets/' |
153 | === modified file 'src/Ubuntu/Web/CMakeLists.txt' |
154 | --- src/Ubuntu/Web/CMakeLists.txt 2015-06-22 10:29:20 +0000 |
155 | +++ src/Ubuntu/Web/CMakeLists.txt 2015-08-27 14:01:06 +0000 |
156 | @@ -20,9 +20,6 @@ |
157 | file(GLOB QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml qmldir *.js) |
158 | install(TARGETS ${PLUGIN} DESTINATION ${UBUNTU_WEB_IMPORTS_DIR}) |
159 | install(FILES ${QML_FILES} DESTINATION ${UBUNTU_WEB_IMPORTS_DIR}) |
160 | -install(DIRECTORY assets |
161 | - DESTINATION ${CMAKE_INSTALL_DATADIR}/qtdeclarative5-ubuntu-web-plugin |
162 | - FILES_MATCHING PATTERN *.png) |
163 | |
164 | if(NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) |
165 | # copy qml files over to build dir to be able to import them uninstalled |
166 | |
167 | === removed file 'src/Ubuntu/Web/Selection.qml' |
168 | --- src/Ubuntu/Web/Selection.qml 2015-08-10 15:22:00 +0000 |
169 | +++ src/Ubuntu/Web/Selection.qml 1970-01-01 00:00:00 +0000 |
170 | @@ -1,161 +0,0 @@ |
171 | -/* |
172 | - * Copyright 2013-2015 Canonical Ltd. |
173 | - * |
174 | - * This file is part of webbrowser-app. |
175 | - * |
176 | - * webbrowser-app is free software; you can redistribute it and/or modify |
177 | - * it under the terms of the GNU General Public License as published by |
178 | - * the Free Software Foundation; version 3. |
179 | - * |
180 | - * webbrowser-app is distributed in the hope that it will be useful, |
181 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
182 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
183 | - * GNU General Public License for more details. |
184 | - * |
185 | - * You should have received a copy of the GNU General Public License |
186 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
187 | - */ |
188 | - |
189 | -import QtQuick 2.4 |
190 | -import Ubuntu.Components 1.3 |
191 | - |
192 | -Item { |
193 | - id: __container |
194 | - |
195 | - property alias rect: __rect |
196 | - |
197 | - property real __minimumWidth: units.gu(5) |
198 | - property real __minimumHeight: units.gu(5) |
199 | - |
200 | - readonly property bool resizing: __leftHandle.dragging || __topHandle.dragging || __rightHandle.dragging || __bottomHandle.dragging |
201 | - |
202 | - signal resized() |
203 | - signal dismissed() |
204 | - |
205 | - MouseArea { |
206 | - anchors.fill: parent |
207 | - // dismiss the selection when tapping anywhere except for the handles |
208 | - onClicked: __container.dismissed() |
209 | - } |
210 | - |
211 | - Item { |
212 | - id: __rect |
213 | - objectName: "rectangle" |
214 | - } |
215 | - |
216 | - Rectangle { |
217 | - id: __outline |
218 | - |
219 | - color: "transparent" |
220 | - |
221 | - Rectangle { |
222 | - anchors.fill: parent |
223 | - radius: parent.radius |
224 | - color: parent.border.color |
225 | - opacity: 0.1 |
226 | - z: -1 |
227 | - } |
228 | - |
229 | - border { |
230 | - width: units.dp(3) |
231 | - color: "#19B6EE" |
232 | - } |
233 | - radius: units.dp(3) |
234 | - antialiasing: true |
235 | - |
236 | - x: __rect.x |
237 | - width: { |
238 | - if (__leftHandle.dragging) { |
239 | - return __rect.x + __rect.width - (__leftHandle.x + __leftHandle.width / 2) |
240 | - } else if (__rightHandle.dragging) { |
241 | - return __rightHandle.x + __rightHandle.width / 2 - __rect.x |
242 | - } else { |
243 | - return __rect.width |
244 | - } |
245 | - } |
246 | - |
247 | - y: __rect.y |
248 | - height: { |
249 | - if (__topHandle.dragging) { |
250 | - return __rect.y + __rect.height - (__topHandle.y + __topHandle.height / 2) |
251 | - } else if (__bottomHandle.dragging) { |
252 | - return __bottomHandle.y + __bottomHandle.height / 2 - __rect.y |
253 | - } else { |
254 | - return __rect.height |
255 | - } |
256 | - } |
257 | - |
258 | - anchors { |
259 | - left: __rightHandle.dragging ? __rect.left: undefined |
260 | - right: __leftHandle.dragging ? __rect.right : undefined |
261 | - top: __bottomHandle.dragging ? __rect.top : undefined |
262 | - bottom: __topHandle.dragging ? __rect.bottom : undefined |
263 | - } |
264 | - } |
265 | - |
266 | - SelectionHandle { |
267 | - id: __leftHandle |
268 | - objectName: "leftHandle" |
269 | - axis: Drag.XAxis |
270 | - x: __rect.x - width / 2 |
271 | - y: (__topHandle.y + __bottomHandle.y) / 2 |
272 | - minimum: 0 |
273 | - maximum: __rightHandle.x - __container.__minimumWidth |
274 | - onDraggingChanged: { |
275 | - if (!dragging) { |
276 | - __rect.width = __rightHandle.x - __leftHandle.x |
277 | - __rect.x = __leftHandle.x + __leftHandle.width / 2 |
278 | - __container.resized() |
279 | - } |
280 | - } |
281 | - } |
282 | - |
283 | - SelectionHandle { |
284 | - id: __topHandle |
285 | - objectName: "topHandle" |
286 | - axis: Drag.YAxis |
287 | - x: (__leftHandle.x + __rightHandle.x) / 2 |
288 | - y: __rect.y - height / 2 |
289 | - minimum: 0 |
290 | - maximum: __bottomHandle.y - __container.__minimumHeight |
291 | - onDraggingChanged: { |
292 | - if (!dragging) { |
293 | - __rect.height = __bottomHandle.y - __topHandle.y |
294 | - __rect.y = __topHandle.y + __topHandle.height / 2 |
295 | - __container.resized() |
296 | - } |
297 | - } |
298 | - } |
299 | - |
300 | - SelectionHandle { |
301 | - id: __rightHandle |
302 | - objectName: "rightHandle" |
303 | - axis: Drag.XAxis |
304 | - x: __rect.x + __rect.width - width / 2 |
305 | - y: (__topHandle.y + __bottomHandle.y) / 2 |
306 | - minimum: __leftHandle.x + __container.__minimumWidth |
307 | - maximum: __container.width |
308 | - onDraggingChanged: { |
309 | - if (!dragging) { |
310 | - __rect.width = __rightHandle.x - __leftHandle.x |
311 | - __container.resized() |
312 | - } |
313 | - } |
314 | - } |
315 | - |
316 | - SelectionHandle { |
317 | - id: __bottomHandle |
318 | - objectName: "bottomHandle" |
319 | - axis: Drag.YAxis |
320 | - x: (__leftHandle.x + __rightHandle.x) / 2 |
321 | - y: __rect.y + __rect.height - height / 2 |
322 | - minimum: __topHandle.y + __container.__minimumHeight |
323 | - maximum: __container.height |
324 | - onDraggingChanged: { |
325 | - if (!dragging) { |
326 | - __rect.height = __bottomHandle.y - __topHandle.y |
327 | - __container.resized() |
328 | - } |
329 | - } |
330 | - } |
331 | -} |
332 | |
333 | === removed file 'src/Ubuntu/Web/SelectionHandle.qml' |
334 | --- src/Ubuntu/Web/SelectionHandle.qml 2015-08-10 15:22:00 +0000 |
335 | +++ src/Ubuntu/Web/SelectionHandle.qml 1970-01-01 00:00:00 +0000 |
336 | @@ -1,47 +0,0 @@ |
337 | -/* |
338 | - * Copyright 2013-2015 Canonical Ltd. |
339 | - * |
340 | - * This file is part of webbrowser-app. |
341 | - * |
342 | - * webbrowser-app is free software; you can redistribute it and/or modify |
343 | - * it under the terms of the GNU General Public License as published by |
344 | - * the Free Software Foundation; version 3. |
345 | - * |
346 | - * webbrowser-app is distributed in the hope that it will be useful, |
347 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
348 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
349 | - * GNU General Public License for more details. |
350 | - * |
351 | - * You should have received a copy of the GNU General Public License |
352 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
353 | - */ |
354 | - |
355 | -import QtQuick 2.4 |
356 | -import Ubuntu.Components 1.3 |
357 | - |
358 | -Image { |
359 | - property int axis |
360 | - property real minimum |
361 | - property real maximum |
362 | - property bool dragging: __mousearea.drag.active |
363 | - |
364 | - width: units.gu(3) |
365 | - height: units.gu(3) |
366 | - |
367 | - source: "assets/multi_selection_handle.png" |
368 | - |
369 | - MouseArea { |
370 | - id: __mousearea |
371 | - |
372 | - anchors.fill: parent |
373 | - |
374 | - drag { |
375 | - target: parent |
376 | - axis: parent.axis |
377 | - minimumX: parent.minimum |
378 | - maximumX: parent.maximum |
379 | - minimumY: parent.minimum |
380 | - maximumY: parent.maximum |
381 | - } |
382 | - } |
383 | -} |
384 | |
385 | === modified file 'src/Ubuntu/Web/UbuntuWebView02.qml' |
386 | --- src/Ubuntu/Web/UbuntuWebView02.qml 2015-08-13 19:06:11 +0000 |
387 | +++ src/Ubuntu/Web/UbuntuWebView02.qml 2015-08-27 14:01:06 +0000 |
388 | @@ -18,7 +18,7 @@ |
389 | |
390 | import QtQuick 2.4 |
391 | import QtQuick.Window 2.2 |
392 | -import com.canonical.Oxide 1.5 as Oxide |
393 | +import com.canonical.Oxide 1.8 as Oxide |
394 | import Ubuntu.Components 1.3 |
395 | import Ubuntu.Components.Popups 1.3 |
396 | import "." // QTBUG-34418 |
397 | @@ -38,40 +38,10 @@ |
398 | |
399 | messageHandlers: [ |
400 | Oxide.ScriptMessageHandler { |
401 | - msgId: "contextmenu" |
402 | - contexts: ["oxide://selection/"] |
403 | - callback: function(msg, frame) { |
404 | - internal.dismissCurrentContextualMenu() |
405 | - internal.dismissCurrentSelection() |
406 | - internal.fillContextualData(msg.args) |
407 | - if (contextualActions != null) { |
408 | - for (var i = 0; i < contextualActions.actions.length; ++i) { |
409 | - if (contextualActions.actions[i].enabled) { |
410 | - contextualRectangle.position(msg.args) |
411 | - internal.currentContextualMenu = PopupUtils.open(contextualPopover, contextualRectangle) |
412 | - break |
413 | - } |
414 | - } |
415 | - } |
416 | - } |
417 | - }, |
418 | - Oxide.ScriptMessageHandler { |
419 | - msgId: "selection" |
420 | - contexts: ["oxide://selection/"] |
421 | - callback: function(msg, frame) { |
422 | - internal.dismissCurrentSelection() |
423 | - internal.dismissCurrentContextualMenu() |
424 | - if (selectionActions != null) { |
425 | - for (var i = 0; i < selectionActions.actions.length; ++i) { |
426 | - if (selectionActions.actions[i].enabled) { |
427 | - var mimedata = internal.buildMimedata(msg.args) |
428 | - var bounds = internal.computeBounds(msg.args) |
429 | - internal.currentSelection = selection.createObject(_webview, {mimedata: mimedata, bounds: bounds}) |
430 | - internal.currentSelection.showActions() |
431 | - break |
432 | - } |
433 | - } |
434 | - } |
435 | + msgId: "dpr" |
436 | + contexts: ["oxide://selection/"] |
437 | + callback: function(msg, frame) { |
438 | + internal.devicePixelRatio = msg.args.dpr |
439 | } |
440 | }, |
441 | Oxide.ScriptMessageHandler { |
442 | @@ -79,7 +49,6 @@ |
443 | contexts: ["oxide://selection/"] |
444 | callback: function(msg, frame) { |
445 | internal.dismissCurrentContextualMenu() |
446 | - internal.dismissCurrentSelection() |
447 | } |
448 | } |
449 | ] |
450 | @@ -97,16 +66,16 @@ |
451 | |
452 | Item { |
453 | id: contextualRectangle |
454 | - |
455 | visible: false |
456 | + readonly property real locationBarOffset: _webview.locationBarController.height + _webview.locationBarController.offset |
457 | + // XXX: Because the context model’s position is incorrectly reported in |
458 | + // device-independent pixels (see https://launchpad.net/bugs/1471181), |
459 | + // it needs to be multiplied by the device pixel ratio to get physical pixels. |
460 | + x: internal.contextModel ? internal.contextModel.position.x * internal.devicePixelRatio : 0 |
461 | + y: internal.contextModel ? internal.contextModel.position.y * internal.devicePixelRatio + locationBarOffset : 0 |
462 | + } |
463 | |
464 | - function position(data) { |
465 | - x = data.left * data.scaleX |
466 | - y = data.top * data.scaleY |
467 | - width = data.width * data.scaleX |
468 | - height = data.height * data.scaleY |
469 | - } |
470 | - } |
471 | + // XXX: This property is deprecated in favour of contextModel. |
472 | property QtObject contextualData: QtObject { |
473 | property url href |
474 | property string title |
475 | @@ -120,134 +89,71 @@ |
476 | } |
477 | |
478 | property var contextualActions // type: ActionList |
479 | - Component { |
480 | - id: contextualPopover |
481 | - ActionSelectionPopover { |
482 | - actions: contextualActions |
483 | + contextMenu: ActionSelectionPopover { |
484 | + objectName: "contextMenu" |
485 | + actions: contextualActions |
486 | + caller: contextualRectangle |
487 | + Component.onCompleted: { |
488 | + internal.dismissCurrentContextualMenu() |
489 | + internal.contextModel = model |
490 | + var empty = true |
491 | + if (actions) { |
492 | + for (var i in actions.actions) { |
493 | + if (actions.actions[i].enabled) { |
494 | + empty = false |
495 | + break |
496 | + } |
497 | + } |
498 | + } |
499 | + if (empty) { |
500 | + internal.dismissCurrentContextualMenu() |
501 | + } else { |
502 | + contextualData.clear() |
503 | + contextualData.href = model.linkUrl |
504 | + contextualData.title = model.linkText |
505 | + if ((model.mediaType == Oxide.WebView.MediaTypeImage) && model.hasImageContents) { |
506 | + contextualData.img = model.srcUrl |
507 | + } |
508 | + show() |
509 | + } |
510 | + } |
511 | + onVisibleChanged: { |
512 | + if (!visible) { |
513 | + internal.dismissCurrentContextualMenu() |
514 | + } |
515 | } |
516 | } |
517 | + readonly property QtObject contextModel: internal.contextModel |
518 | |
519 | property var selectionActions // type: ActionList |
520 | - onSelectionActionsChanged: { |
521 | - for (var i in selectionActions.actions) { |
522 | - selectionActions.actions[i].onTriggered.connect(function () { |
523 | - internal.dismissCurrentSelection() |
524 | - }) |
525 | - } |
526 | - } |
527 | - Component { |
528 | - id: selection |
529 | - Selection { |
530 | - anchors.fill: parent |
531 | - property var mimedata: null |
532 | - property rect bounds |
533 | - onBoundsChanged: { |
534 | - rect.x = bounds.x |
535 | - rect.y = bounds.y |
536 | - rect.width = bounds.width |
537 | - rect.height = bounds.height |
538 | - } |
539 | - property Item actions: null |
540 | - Component { |
541 | - id: selectionPopover |
542 | - ActionSelectionPopover { |
543 | - objectName: "selectionActions" |
544 | - autoClose: false |
545 | - actions: selectionActions |
546 | - } |
547 | - } |
548 | - function showActions() { |
549 | - if (actions != null) { |
550 | - actions.destroy() |
551 | - } |
552 | - actions = PopupUtils.open(selectionPopover, rect) |
553 | - } |
554 | - onResizingChanged: { |
555 | - if (resizing) { |
556 | - if (actions != null) { |
557 | - actions.destroy() |
558 | - } |
559 | - } |
560 | - } |
561 | - onResized: { |
562 | - var args = {x: rect.x, y: rect.y, width: rect.width, height: rect.height} |
563 | - var msg = _webview.rootFrame.sendMessage("oxide://selection/", "adjustselection", args) |
564 | - msg.onreply = function(response) { |
565 | - internal.currentSelection.mimedata = internal.buildMimedata(response) |
566 | - // Ensure that the bounds are updated |
567 | - internal.currentSelection.bounds = Qt.rect(0, 0, 0, 0) |
568 | - internal.currentSelection.bounds = internal.computeBounds(response) |
569 | - internal.currentSelection.showActions() |
570 | - } |
571 | - msg.onerror = function(error) { |
572 | - internal.dismissCurrentSelection() |
573 | - } |
574 | - } |
575 | - onDismissed: internal.dismissCurrentSelection() |
576 | - } |
577 | - } |
578 | + onSelectionActionsChanged: console.warn("WARNING: the 'selectionActions' property is deprecated and ignored.") |
579 | function copy() { |
580 | - if (internal.currentSelection != null) { |
581 | - Clipboard.push(internal.currentSelection.mimedata) |
582 | - } else { |
583 | - console.warn("No current selection") |
584 | - } |
585 | + console.warn("WARNING: the copy() function is deprecated and does nothing.") |
586 | } |
587 | |
588 | + readonly property real devicePixelRatio: internal.devicePixelRatio |
589 | + |
590 | QtObject { |
591 | id: internal |
592 | property int lastLoadRequestStatus: -1 |
593 | - property Item currentContextualMenu: null |
594 | - property Item currentSelection: null |
595 | - |
596 | - function fillContextualData(data) { |
597 | - contextualData.clear() |
598 | - if ('img' in data) { |
599 | - contextualData.img = data.img |
600 | - } |
601 | - if ('href' in data) { |
602 | - contextualData.href = data.href |
603 | - contextualData.title = data.title |
604 | - } |
605 | - } |
606 | - |
607 | - function buildMimedata(data) { |
608 | - var mimedata = Clipboard.newData() |
609 | - if ('html' in data) { |
610 | - mimedata.html = data.html |
611 | - } |
612 | - // FIXME: push the text and image data in the order |
613 | - // they appear in the selected block. |
614 | - if ('text' in data) { |
615 | - mimedata.text = data.text |
616 | - } |
617 | - if ('images' in data) { |
618 | - // TODO: download and cache the images locally |
619 | - // (grab them from the webview’s cache, if possible), |
620 | - // and forward local URLs. |
621 | - mimedata.urls = data.images |
622 | - } |
623 | - return mimedata |
624 | - } |
625 | + property QtObject contextModel: null |
626 | + property real devicePixelRatio: 1.0 |
627 | |
628 | function computeBounds(data) { |
629 | - return Qt.rect(data.left * data.scaleX, data.top * data.scaleY, |
630 | - data.width * data.scaleX, data.height * data.scaleY) |
631 | + var locationBarOffset = _webview.locationBarController.height + _webview.locationBarController.offset |
632 | + var scaleX = data.outerWidth / data.innerWidth * internal.devicePixelRatio |
633 | + var scaleY = data.outerHeight / (data.innerHeight + locationBarOffset) * internal.devicePixelRatio |
634 | + return Qt.rect(data.left * scaleX, data.top * scaleY + locationBarOffset, |
635 | + data.width * scaleX, data.height * scaleY) |
636 | } |
637 | |
638 | function dismissCurrentContextualMenu() { |
639 | - if (currentContextualMenu != null) { |
640 | - PopupUtils.close(currentContextualMenu) |
641 | + if (contextModel) { |
642 | + contextModel.close() |
643 | } |
644 | } |
645 | |
646 | - function dismissCurrentSelection() { |
647 | - if (currentSelection != null) { |
648 | - // For some reason a 0 delay fails to destroy the selection |
649 | - // when it was requested upon a screen orientation change… |
650 | - currentSelection.destroy(1) |
651 | - } |
652 | - } |
653 | + onContextModelChanged: if (!contextModel) _webview.contextualData.clear() |
654 | } |
655 | |
656 | readonly property bool lastLoadSucceeded: internal.lastLoadRequestStatus === Oxide.LoadEvent.TypeSucceeded |
657 | @@ -258,13 +164,11 @@ |
658 | internal.lastLoadRequestStatus = event.type |
659 | } |
660 | internal.dismissCurrentContextualMenu() |
661 | - internal.dismissCurrentSelection() |
662 | } |
663 | |
664 | readonly property int screenOrientation: Screen.orientation |
665 | onScreenOrientationChanged: { |
666 | internal.dismissCurrentContextualMenu() |
667 | - internal.dismissCurrentSelection() |
668 | } |
669 | |
670 | onJavaScriptConsoleMessage: { |
671 | |
672 | === removed directory 'src/Ubuntu/Web/assets' |
673 | === removed file 'src/Ubuntu/Web/assets/multi_selection_handle@20.png' |
674 | Binary files src/Ubuntu/Web/assets/multi_selection_handle@20.png 2013-02-07 11:56:01 +0000 and src/Ubuntu/Web/assets/multi_selection_handle@20.png 1970-01-01 00:00:00 +0000 differ |
675 | === modified file 'src/Ubuntu/Web/selection02.js' |
676 | --- src/Ubuntu/Web/selection02.js 2015-01-23 13:26:40 +0000 |
677 | +++ src/Ubuntu/Web/selection02.js 2015-08-27 14:01:06 +0000 |
678 | @@ -1,5 +1,5 @@ |
679 | /* |
680 | - * Copyright 2013-2014 Canonical Ltd. |
681 | + * Copyright 2013-2015 Canonical Ltd. |
682 | * |
683 | * This file is part of webbrowser-app. |
684 | * |
685 | @@ -16,137 +16,8 @@ |
686 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
687 | */ |
688 | |
689 | -function elementContainedInBox(element, box) { |
690 | - var rect = element.getBoundingClientRect(); |
691 | - return ((box.left <= rect.left) && (box.right >= rect.right) && |
692 | - (box.top <= rect.top) && (box.bottom >= rect.bottom)); |
693 | -} |
694 | - |
695 | -function getImgFullUri(uri) { |
696 | - if ((uri.slice(0, 7) === 'http://') || |
697 | - (uri.slice(0, 8) === 'https://') || |
698 | - (uri.slice(0, 7) === 'file://') || |
699 | - (uri.slice(0, 5) === 'data:')) { |
700 | - return uri; |
701 | - } else if (uri.slice(0, 1) === '/') { |
702 | - var docuri = document.documentURI; |
703 | - var firstcolon = docuri.indexOf('://'); |
704 | - var protocol = 'http://'; |
705 | - if (firstcolon !== -1) { |
706 | - protocol = docuri.slice(0, firstcolon + 3); |
707 | - } |
708 | - if (uri.slice(0, 2) === '//') { |
709 | - // URLs beginning with a // should just inherit the protocol |
710 | - // from the current page |
711 | - return protocol + uri.slice(2); |
712 | - } else { |
713 | - return protocol + document.domain + uri; |
714 | - } |
715 | - } else { |
716 | - var base = document.baseURI; |
717 | - var lastslash = base.lastIndexOf('/'); |
718 | - if (lastslash === -1) { |
719 | - return base + '/' + uri; |
720 | - } else { |
721 | - return base.slice(0, lastslash + 1) + uri; |
722 | - } |
723 | - } |
724 | -} |
725 | - |
726 | -function getSelectedData(element) { |
727 | - var node = element; |
728 | - var data = new Object; |
729 | - |
730 | - var nodeName = node.nodeName.toLowerCase(); |
731 | - if (nodeName === 'img') { |
732 | - data.img = getImgFullUri(node.getAttribute('src')); |
733 | - } else if (nodeName === 'a') { |
734 | - data.href = node.href; |
735 | - data.title = node.title; |
736 | - } |
737 | - |
738 | - // If the parent tag is a hyperlink, we want it too. |
739 | - var parent = node.parentNode; |
740 | - if ((nodeName !== 'a') && parent && (parent.nodeName.toLowerCase() === 'a')) { |
741 | - data.href = parent.href; |
742 | - data.title = parent.title; |
743 | - node = parent; |
744 | - } |
745 | - |
746 | - var boundingRect = node.getBoundingClientRect(); |
747 | - data.left = boundingRect.left; |
748 | - data.top = boundingRect.top; |
749 | - data.width = boundingRect.width; |
750 | - data.height = boundingRect.height; |
751 | - |
752 | - node = node.cloneNode(true); |
753 | - // filter out script nodes |
754 | - var scripts = node.getElementsByTagName('script'); |
755 | - while (scripts.length > 0) { |
756 | - var scriptNode = scripts[0]; |
757 | - if (scriptNode.parentNode) { |
758 | - scriptNode.parentNode.removeChild(scriptNode); |
759 | - } |
760 | - } |
761 | - data.html = node.outerHTML; |
762 | - data.nodeName = node.nodeName.toLowerCase(); |
763 | - // FIXME: extract the text and images in the order they appear in the block, |
764 | - // so that this order is respected when the data is pushed to the clipboard. |
765 | - data.text = node.textContent; |
766 | - var images = []; |
767 | - var imgs = node.getElementsByTagName('img'); |
768 | - for (var i = 0; i < imgs.length; i++) { |
769 | - images.push(getImgFullUri(imgs[i].getAttribute('src'))); |
770 | - } |
771 | - if (images.length > 0) { |
772 | - data.images = images; |
773 | - } |
774 | - |
775 | - return data; |
776 | -} |
777 | - |
778 | -function adjustSelection(selection) { |
779 | - // FIXME: allow selecting two consecutive blocks, instead of |
780 | - // interpolating to the containing block. |
781 | - var centerX = (selection.left + selection.right) / 2; |
782 | - var centerY = (selection.top + selection.bottom) / 2; |
783 | - var element = document.elementFromPoint(centerX, centerY); |
784 | - var parent = element; |
785 | - while (elementContainedInBox(parent, selection)) { |
786 | - parent = parent.parentNode; |
787 | - } |
788 | - element = parent; |
789 | - return getSelectedData(element); |
790 | -} |
791 | - |
792 | -document.documentElement.addEventListener('contextmenu', function(event) { |
793 | - var element = document.elementFromPoint(event.clientX, event.clientY); |
794 | - var data = getSelectedData(element); |
795 | - var w = document.defaultView; |
796 | - data['scaleX'] = w.outerWidth / w.innerWidth * w.devicePixelRatio; |
797 | - data['scaleY'] = w.outerHeight / w.innerHeight * w.devicePixelRatio; |
798 | - if (('img' in data) || ('href' in data)) { |
799 | - oxide.sendMessage('contextmenu', data); |
800 | - } else { |
801 | - oxide.sendMessage('selection', data); |
802 | - } |
803 | -}); |
804 | - |
805 | document.defaultView.addEventListener('scroll', function(event) { |
806 | oxide.sendMessage('scroll', {}); |
807 | }); |
808 | |
809 | -oxide.addMessageHandler("adjustselection", function (msg) { |
810 | - var w = document.defaultView; |
811 | - var scaleX = w.outerWidth / w.innerWidth * w.devicePixelRatio; |
812 | - var scaleY = w.outerHeight / w.innerHeight * w.devicePixelRatio; |
813 | - var selection = new Object; |
814 | - selection.left = msg.args.x / scaleX; |
815 | - selection.right = selection.left + msg.args.width / scaleX; |
816 | - selection.top = msg.args.y / scaleY; |
817 | - selection.bottom = selection.top + msg.args.height / scaleY; |
818 | - var adjusted = adjustSelection(selection); |
819 | - adjusted['scaleX'] = scaleX; |
820 | - adjusted['scaleY'] = scaleY; |
821 | - msg.reply(adjusted); |
822 | -}); |
823 | +oxide.sendMessage('dpr', {dpr: document.defaultView.devicePixelRatio}); |
824 | |
825 | === modified file 'src/app/CMakeLists.txt' |
826 | --- src/app/CMakeLists.txt 2015-06-22 10:29:20 +0000 |
827 | +++ src/app/CMakeLists.txt 2015-08-27 14:01:06 +0000 |
828 | @@ -19,6 +19,7 @@ |
829 | set(COMMONLIB_SRC |
830 | browserapplication.cpp |
831 | favicon-fetcher.cpp |
832 | + mime-database.cpp |
833 | session-storage.cpp |
834 | webbrowser-window.cpp |
835 | ) |
836 | |
837 | === modified file 'src/app/FileExtensionMapper.js' |
838 | --- src/app/FileExtensionMapper.js 2014-11-11 15:07:46 +0000 |
839 | +++ src/app/FileExtensionMapper.js 2015-08-27 14:01:06 +0000 |
840 | @@ -1,5 +1,5 @@ |
841 | /* |
842 | - * Copyright 2014 Canonical Ltd. |
843 | + * Copyright 2014-2015 Canonical Ltd. |
844 | * |
845 | * This file is part of webbrowser-app. |
846 | * |
847 | @@ -15,15 +15,19 @@ |
848 | * You should have received a copy of the GNU General Public License |
849 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
850 | */ |
851 | +'use strict'; |
852 | + |
853 | +function getExtension(filename) { |
854 | + var filenameParts = filename.split("."); |
855 | + if (filenameParts.length === 1 || (filenameParts[0] === "" && filenameParts.length === 2)) { |
856 | + return "" |
857 | + } |
858 | + return filenameParts.pop().toLowerCase(); |
859 | +} |
860 | |
861 | // Constructed from /etc/mime.types |
862 | function filenameToContentType(filename) { |
863 | - var filenameParts = filename.split("."); |
864 | - if(filenameParts.length === 1 || (filenameParts[0] === "" && filenameParts.length === 2)) { |
865 | - return ContentType.Unknown; |
866 | - } |
867 | - var ext = filenameParts.pop().toLowerCase(); |
868 | - switch(ext) { |
869 | + switch(getExtension(filename)) { |
870 | case "art": |
871 | case "bmp": |
872 | case "cdr": |
873 | |
874 | === modified file 'src/app/FilePickerDialog.qml' |
875 | --- src/app/FilePickerDialog.qml 2015-08-10 15:22:00 +0000 |
876 | +++ src/app/FilePickerDialog.qml 2015-08-27 14:01:06 +0000 |
877 | @@ -27,7 +27,7 @@ |
878 | id: fileDialog |
879 | title: i18n.tr("Please choose a file") |
880 | selectMultiple: model.allowMultipleFiles |
881 | - |
882 | + |
883 | onAccepted: { |
884 | var selectedFiles = [] |
885 | for(var i in fileDialog.fileUrls) { |
886 | @@ -35,7 +35,7 @@ |
887 | } |
888 | model.accept(selectedFiles) |
889 | } |
890 | - |
891 | + |
892 | onRejected: { |
893 | model.reject() |
894 | } |
895 | |
896 | === modified file 'src/app/WebViewImpl.qml' |
897 | --- src/app/WebViewImpl.qml 2015-08-10 15:22:00 +0000 |
898 | +++ src/app/WebViewImpl.qml 2015-08-27 14:01:06 +0000 |
899 | @@ -20,6 +20,7 @@ |
900 | import Ubuntu.Components 1.3 |
901 | import Ubuntu.Components.Popups 1.3 |
902 | import Ubuntu.Web 0.2 |
903 | +import webbrowsercommon.private 0.1 |
904 | import "actions" as Actions |
905 | |
906 | WebView { |
907 | @@ -54,14 +55,27 @@ |
908 | |
909 | if (downloadLoader.status == Loader.Ready) { |
910 | var headers = { } |
911 | - if(request.cookies.length > 0) { |
912 | + if (request.cookies.length > 0) { |
913 | headers["Cookie"] = request.cookies.join(";") |
914 | } |
915 | - if(request.referrer) { |
916 | + if (request.referrer) { |
917 | headers["Referer"] = request.referrer |
918 | } |
919 | headers["User-Agent"] = webview.context.userAgent |
920 | - downloadLoader.item.downloadMimeType(request.url, request.mimeType, headers, request.suggestedFilename) |
921 | + // Work around https://launchpad.net/bugs/1487090 by guessing the mime type |
922 | + // from the suggested filename or URL if oxide hasn’t provided one. |
923 | + var mimeType = request.mimeType |
924 | + if (!mimeType) { |
925 | + mimeType = MimeDatabase.filenameToMimeType(request.suggestedFilename) |
926 | + } |
927 | + if (!mimeType) { |
928 | + var scheme = request.url.toString().split('://').shift().toLowerCase() |
929 | + var filename = request.url.toString().split('/').pop().split('?').shift() |
930 | + if ((scheme == "file") || (filename.indexOf('.') > -1)) { |
931 | + mimeType = MimeDatabase.filenameToMimeType(filename) |
932 | + } |
933 | + } |
934 | + downloadLoader.item.downloadMimeType(request.url, mimeType, headers, request.suggestedFilename) |
935 | } else { |
936 | // Desktop form factor case |
937 | Qt.openUrlExternally(request.url) |
938 | @@ -76,16 +90,13 @@ |
939 | |
940 | Loader { |
941 | id: downloadLoader |
942 | + // TODO: Use the ubuntu download manager on desktop as well |
943 | + // (https://launchpad.net/bugs/1477310). This will require to have |
944 | + // ubuntu-download-manager in main (https://launchpad.net/bugs/1488425). |
945 | source: formFactor == "desktop" ? "" : "Downloader.qml" |
946 | asynchronous: true |
947 | } |
948 | |
949 | - selectionActions: ActionList { |
950 | - Actions.Copy { |
951 | - onTriggered: copy() |
952 | - } |
953 | - } |
954 | - |
955 | function requestGeolocationPermission(request) { |
956 | PopupUtils.open(Qt.resolvedUrl("GeolocationPermissionRequest.qml"), |
957 | webview.currentWebview, {"request": request}) |
958 | |
959 | === added file 'src/app/actions/Cut.qml' |
960 | --- src/app/actions/Cut.qml 1970-01-01 00:00:00 +0000 |
961 | +++ src/app/actions/Cut.qml 2015-08-27 14:01:06 +0000 |
962 | @@ -0,0 +1,23 @@ |
963 | +/* |
964 | + * Copyright 2015 Canonical Ltd. |
965 | + * |
966 | + * This file is part of webbrowser-app. |
967 | + * |
968 | + * webbrowser-app is free software; you can redistribute it and/or modify |
969 | + * it under the terms of the GNU General Public License as published by |
970 | + * the Free Software Foundation; version 3. |
971 | + * |
972 | + * webbrowser-app is distributed in the hope that it will be useful, |
973 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
974 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
975 | + * GNU General Public License for more details. |
976 | + * |
977 | + * You should have received a copy of the GNU General Public License |
978 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
979 | + */ |
980 | + |
981 | +import Ubuntu.Components 1.3 |
982 | + |
983 | +Action { |
984 | + text: i18n.tr("Cut") |
985 | +} |
986 | |
987 | === added file 'src/app/actions/Erase.qml' |
988 | --- src/app/actions/Erase.qml 1970-01-01 00:00:00 +0000 |
989 | +++ src/app/actions/Erase.qml 2015-08-27 14:01:06 +0000 |
990 | @@ -0,0 +1,23 @@ |
991 | +/* |
992 | + * Copyright 2015 Canonical Ltd. |
993 | + * |
994 | + * This file is part of webbrowser-app. |
995 | + * |
996 | + * webbrowser-app is free software; you can redistribute it and/or modify |
997 | + * it under the terms of the GNU General Public License as published by |
998 | + * the Free Software Foundation; version 3. |
999 | + * |
1000 | + * webbrowser-app is distributed in the hope that it will be useful, |
1001 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1002 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1003 | + * GNU General Public License for more details. |
1004 | + * |
1005 | + * You should have received a copy of the GNU General Public License |
1006 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1007 | + */ |
1008 | + |
1009 | +import Ubuntu.Components 1.3 |
1010 | + |
1011 | +Action { |
1012 | + text: i18n.tr("Erase") |
1013 | +} |
1014 | |
1015 | === added file 'src/app/actions/Paste.qml' |
1016 | --- src/app/actions/Paste.qml 1970-01-01 00:00:00 +0000 |
1017 | +++ src/app/actions/Paste.qml 2015-08-27 14:01:06 +0000 |
1018 | @@ -0,0 +1,23 @@ |
1019 | +/* |
1020 | + * Copyright 2015 Canonical Ltd. |
1021 | + * |
1022 | + * This file is part of webbrowser-app. |
1023 | + * |
1024 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1025 | + * it under the terms of the GNU General Public License as published by |
1026 | + * the Free Software Foundation; version 3. |
1027 | + * |
1028 | + * webbrowser-app is distributed in the hope that it will be useful, |
1029 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1030 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1031 | + * GNU General Public License for more details. |
1032 | + * |
1033 | + * You should have received a copy of the GNU General Public License |
1034 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1035 | + */ |
1036 | + |
1037 | +import Ubuntu.Components 1.3 |
1038 | + |
1039 | +Action { |
1040 | + text: i18n.tr("Paste") |
1041 | +} |
1042 | |
1043 | === added file 'src/app/actions/Redo.qml' |
1044 | --- src/app/actions/Redo.qml 1970-01-01 00:00:00 +0000 |
1045 | +++ src/app/actions/Redo.qml 2015-08-27 14:01:06 +0000 |
1046 | @@ -0,0 +1,23 @@ |
1047 | +/* |
1048 | + * Copyright 2015 Canonical Ltd. |
1049 | + * |
1050 | + * This file is part of webbrowser-app. |
1051 | + * |
1052 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1053 | + * it under the terms of the GNU General Public License as published by |
1054 | + * the Free Software Foundation; version 3. |
1055 | + * |
1056 | + * webbrowser-app is distributed in the hope that it will be useful, |
1057 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1058 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1059 | + * GNU General Public License for more details. |
1060 | + * |
1061 | + * You should have received a copy of the GNU General Public License |
1062 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1063 | + */ |
1064 | + |
1065 | +import Ubuntu.Components 1.3 |
1066 | + |
1067 | +Action { |
1068 | + text: i18n.tr("Redo") |
1069 | +} |
1070 | |
1071 | === added file 'src/app/actions/SaveLink.qml' |
1072 | --- src/app/actions/SaveLink.qml 1970-01-01 00:00:00 +0000 |
1073 | +++ src/app/actions/SaveLink.qml 2015-08-27 14:01:06 +0000 |
1074 | @@ -0,0 +1,23 @@ |
1075 | +/* |
1076 | + * Copyright 2015 Canonical Ltd. |
1077 | + * |
1078 | + * This file is part of webbrowser-app. |
1079 | + * |
1080 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1081 | + * it under the terms of the GNU General Public License as published by |
1082 | + * the Free Software Foundation; version 3. |
1083 | + * |
1084 | + * webbrowser-app is distributed in the hope that it will be useful, |
1085 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1086 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1087 | + * GNU General Public License for more details. |
1088 | + * |
1089 | + * You should have received a copy of the GNU General Public License |
1090 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1091 | + */ |
1092 | + |
1093 | +import Ubuntu.Components 1.3 |
1094 | + |
1095 | +Action { |
1096 | + text: i18n.tr("Save link") |
1097 | +} |
1098 | |
1099 | === added file 'src/app/actions/SelectAll.qml' |
1100 | --- src/app/actions/SelectAll.qml 1970-01-01 00:00:00 +0000 |
1101 | +++ src/app/actions/SelectAll.qml 2015-08-27 14:01:06 +0000 |
1102 | @@ -0,0 +1,23 @@ |
1103 | +/* |
1104 | + * Copyright 2015 Canonical Ltd. |
1105 | + * |
1106 | + * This file is part of webbrowser-app. |
1107 | + * |
1108 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1109 | + * it under the terms of the GNU General Public License as published by |
1110 | + * the Free Software Foundation; version 3. |
1111 | + * |
1112 | + * webbrowser-app is distributed in the hope that it will be useful, |
1113 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1114 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1115 | + * GNU General Public License for more details. |
1116 | + * |
1117 | + * You should have received a copy of the GNU General Public License |
1118 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1119 | + */ |
1120 | + |
1121 | +import Ubuntu.Components 1.3 |
1122 | + |
1123 | +Action { |
1124 | + text: i18n.tr("Select all") |
1125 | +} |
1126 | |
1127 | === added file 'src/app/actions/Undo.qml' |
1128 | --- src/app/actions/Undo.qml 1970-01-01 00:00:00 +0000 |
1129 | +++ src/app/actions/Undo.qml 2015-08-27 14:01:06 +0000 |
1130 | @@ -0,0 +1,23 @@ |
1131 | +/* |
1132 | + * Copyright 2015 Canonical Ltd. |
1133 | + * |
1134 | + * This file is part of webbrowser-app. |
1135 | + * |
1136 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1137 | + * it under the terms of the GNU General Public License as published by |
1138 | + * the Free Software Foundation; version 3. |
1139 | + * |
1140 | + * webbrowser-app is distributed in the hope that it will be useful, |
1141 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1142 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1143 | + * GNU General Public License for more details. |
1144 | + * |
1145 | + * You should have received a copy of the GNU General Public License |
1146 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1147 | + */ |
1148 | + |
1149 | +import Ubuntu.Components 1.3 |
1150 | + |
1151 | +Action { |
1152 | + text: i18n.tr("Undo") |
1153 | +} |
1154 | |
1155 | === modified file 'src/app/browserapplication.cpp' |
1156 | --- src/app/browserapplication.cpp 2015-04-09 07:06:50 +0000 |
1157 | +++ src/app/browserapplication.cpp 2015-08-27 14:01:06 +0000 |
1158 | @@ -31,6 +31,7 @@ |
1159 | #include "browserapplication.h" |
1160 | #include "config.h" |
1161 | #include "favicon-fetcher.h" |
1162 | +#include "mime-database.h" |
1163 | #include "session-storage.h" |
1164 | #include "webbrowser-window.h" |
1165 | |
1166 | @@ -99,6 +100,13 @@ |
1167 | return QString(); |
1168 | } |
1169 | |
1170 | +static QObject* MimeDatabase_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine) |
1171 | +{ |
1172 | + Q_UNUSED(engine); |
1173 | + Q_UNUSED(scriptEngine); |
1174 | + return new MimeDatabase(); |
1175 | +} |
1176 | + |
1177 | static QObject* Direction_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine) |
1178 | { |
1179 | Q_UNUSED(engine); |
1180 | @@ -143,6 +151,7 @@ |
1181 | |
1182 | const char* uri = "webbrowsercommon.private"; |
1183 | qmlRegisterType<FaviconFetcher>(uri, 0, 1, "FaviconFetcher"); |
1184 | + qmlRegisterSingletonType<MimeDatabase>(uri, 0, 1, "MimeDatabase", MimeDatabase_singleton_factory); |
1185 | qmlRegisterType<SessionStorage>(uri, 0, 1, "SessionStorage"); |
1186 | |
1187 | const char* gesturesUri = "Ubuntu.Gestures"; |
1188 | |
1189 | === added file 'src/app/mime-database.cpp' |
1190 | --- src/app/mime-database.cpp 1970-01-01 00:00:00 +0000 |
1191 | +++ src/app/mime-database.cpp 2015-08-27 14:01:06 +0000 |
1192 | @@ -0,0 +1,33 @@ |
1193 | +/* |
1194 | + * Copyright 2015 Canonical Ltd. |
1195 | + * |
1196 | + * This file is part of webbrowser-app. |
1197 | + * |
1198 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1199 | + * it under the terms of the GNU General Public License as published by |
1200 | + * the Free Software Foundation; version 3. |
1201 | + * |
1202 | + * webbrowser-app is distributed in the hope that it will be useful, |
1203 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1204 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1205 | + * GNU General Public License for more details. |
1206 | + * |
1207 | + * You should have received a copy of the GNU General Public License |
1208 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1209 | + */ |
1210 | + |
1211 | +#include "mime-database.h" |
1212 | + |
1213 | +MimeDatabase::MimeDatabase(QObject* parent) |
1214 | + : QObject(parent) |
1215 | +{ |
1216 | +} |
1217 | + |
1218 | +QString MimeDatabase::filenameToMimeType(const QString& filename) const |
1219 | +{ |
1220 | + QMimeType type = m_database.mimeTypeForFile(filename, QMimeDatabase::MatchExtension); |
1221 | + if (!type.isDefault()) { |
1222 | + return type.name(); |
1223 | + } |
1224 | + return QString(); |
1225 | +} |
1226 | |
1227 | === added file 'src/app/mime-database.h' |
1228 | --- src/app/mime-database.h 1970-01-01 00:00:00 +0000 |
1229 | +++ src/app/mime-database.h 2015-08-27 14:01:06 +0000 |
1230 | @@ -0,0 +1,39 @@ |
1231 | +/* |
1232 | + * Copyright 2015 Canonical Ltd. |
1233 | + * |
1234 | + * This file is part of webbrowser-app. |
1235 | + * |
1236 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1237 | + * it under the terms of the GNU General Public License as published by |
1238 | + * the Free Software Foundation; version 3. |
1239 | + * |
1240 | + * webbrowser-app is distributed in the hope that it will be useful, |
1241 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1242 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1243 | + * GNU General Public License for more details. |
1244 | + * |
1245 | + * You should have received a copy of the GNU General Public License |
1246 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1247 | + */ |
1248 | + |
1249 | +#ifndef __MIME_DATABASE_H__ |
1250 | +#define __MIME_DATABASE_H__ |
1251 | + |
1252 | +#include <QtCore/QMimeDatabase> |
1253 | +#include <QtCore/QObject> |
1254 | +#include <QtCore/QString> |
1255 | + |
1256 | +class MimeDatabase : public QObject |
1257 | +{ |
1258 | + Q_OBJECT |
1259 | + |
1260 | +public: |
1261 | + explicit MimeDatabase(QObject* parent=0); |
1262 | + |
1263 | + Q_INVOKABLE QString filenameToMimeType(const QString& filename) const; |
1264 | + |
1265 | +private: |
1266 | + QMimeDatabase m_database; |
1267 | +}; |
1268 | + |
1269 | +#endif // __MIME_DATABASE_H__ |
1270 | |
1271 | === modified file 'src/app/webbrowser/Browser.qml' |
1272 | --- src/app/webbrowser/Browser.qml 2015-08-20 08:58:11 +0000 |
1273 | +++ src/app/webbrowser/Browser.qml 2015-08-27 14:01:06 +0000 |
1274 | @@ -19,7 +19,7 @@ |
1275 | import QtQuick 2.4 |
1276 | import QtQuick.Window 2.2 |
1277 | import Qt.labs.settings 1.0 |
1278 | -import com.canonical.Oxide 1.5 as Oxide |
1279 | +import com.canonical.Oxide 1.8 as Oxide |
1280 | import Ubuntu.Components 1.3 |
1281 | import Ubuntu.Components.Popups 1.3 |
1282 | import webbrowserapp.private 0.1 |
1283 | @@ -924,41 +924,124 @@ |
1284 | preferences.localStorageEnabled: true |
1285 | preferences.appCacheEnabled: true |
1286 | |
1287 | + property QtObject contextModel: null |
1288 | contextualActions: ActionList { |
1289 | Actions.OpenLinkInNewTab { |
1290 | - enabled: contextualData.href.toString() |
1291 | - onTriggered: browser.openUrlInNewTab(contextualData.href, true) |
1292 | + objectName: "openLinkInNewTabContextualAction" |
1293 | + enabled: contextModel && contextModel.linkUrl.toString() |
1294 | + onTriggered: browser.openUrlInNewTab(contextModel.linkUrl, true) |
1295 | } |
1296 | Actions.OpenLinkInNewBackgroundTab { |
1297 | - enabled: contextualData.href.toString() && ((settings.allowOpenInBackgroundTab === "true") || |
1298 | - ((settings.allowOpenInBackgroundTab === "default") && (formFactor === "desktop"))) |
1299 | - onTriggered: browser.openUrlInNewTab(contextualData.href, false) |
1300 | + objectName: "openLinkInNewBackgroundTabContextualAction" |
1301 | + enabled: contextModel && contextModel.linkUrl.toString() && |
1302 | + ((settings.allowOpenInBackgroundTab === "true") || |
1303 | + ((settings.allowOpenInBackgroundTab === "default") && |
1304 | + (formFactor === "desktop"))) |
1305 | + onTriggered: browser.openUrlInNewTab(contextModel.linkUrl, false) |
1306 | } |
1307 | Actions.BookmarkLink { |
1308 | - enabled: contextualData.href.toString() && browser.bookmarksModel |
1309 | - onTriggered: bookmarksModel.add(contextualData.href, contextualData.title, "", "") |
1310 | + objectName: "bookmarkLinkContextualAction" |
1311 | + enabled: contextModel && contextModel.linkUrl.toString() && |
1312 | + browser.bookmarksModel |
1313 | + onTriggered: bookmarksModel.add(contextModel.linkUrl, contextModel.linkText, "", "") |
1314 | } |
1315 | Actions.CopyLink { |
1316 | - enabled: contextualData.href.toString() |
1317 | - onTriggered: Clipboard.push(["text/plain", contextualData.href.toString()]) |
1318 | + objectName: "copyLinkContextualAction" |
1319 | + enabled: contextModel && contextModel.linkUrl.toString() |
1320 | + onTriggered: Clipboard.push(["text/plain", contextModel.linkUrl.toString()]) |
1321 | + } |
1322 | + Actions.SaveLink { |
1323 | + objectName: "SaveLinkContextualAction" |
1324 | + enabled: contextModel && contextModel.linkUrl.toString() |
1325 | + onTriggered: contextModel.saveLink() |
1326 | } |
1327 | Actions.ShareLink { |
1328 | - enabled: (formFactor == "mobile") && contextualData.href.toString() |
1329 | - onTriggered: internal.shareLink(contextualData.href.toString(), contextualData.title) |
1330 | + objectName: "ShareLinkContextualAction" |
1331 | + enabled: (formFactor == "mobile") && |
1332 | + contextModel && contextModel.linkUrl.toString() |
1333 | + onTriggered: internal.shareLink(contextModel.linkUrl.toString(), contextModel.linkText) |
1334 | } |
1335 | Actions.OpenImageInNewTab { |
1336 | - enabled: contextualData.img.toString() |
1337 | - onTriggered: browser.openUrlInNewTab(contextualData.img, true) |
1338 | + objectName: "OpenImageInNewTabContextualAction" |
1339 | + enabled: contextModel && contextModel.srcUrl.toString() |
1340 | + onTriggered: browser.openUrlInNewTab(contextModel.srcUrl, true) |
1341 | } |
1342 | Actions.CopyImage { |
1343 | - enabled: contextualData.img.toString() |
1344 | - onTriggered: Clipboard.push(["text/plain", contextualData.img.toString()]) |
1345 | + objectName: "CopyImageContextualAction" |
1346 | + enabled: contextModel && |
1347 | + (contextModel.mediaType === Oxide.WebView.MediaTypeImage) && |
1348 | + contextModel.srcUrl.toString() |
1349 | + onTriggered: Clipboard.push(["text/plain", contextModel.srcUrl.toString()]) |
1350 | } |
1351 | Actions.SaveImage { |
1352 | - enabled: contextualData.img.toString() && downloadLoader.status == Loader.Ready |
1353 | - onTriggered: downloadLoader.item.downloadPicture(contextualData.img) |
1354 | - } |
1355 | - } |
1356 | + objectName: "SaveImageContextualAction" |
1357 | + enabled: contextModel && |
1358 | + ((contextModel.mediaType === Oxide.WebView.MediaTypeImage) || |
1359 | + (contextModel.mediaType === Oxide.WebView.MediaTypeCanvas)) && |
1360 | + contextModel.srcUrl.toString() |
1361 | + onTriggered: contextModel.saveMedia() |
1362 | + } |
1363 | + Actions.Undo { |
1364 | + objectName: "UndoContextualAction" |
1365 | + enabled: contextModel && contextModel.isEditable && |
1366 | + (contextModel.editFlags & Oxide.WebView.UndoCapability) |
1367 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandUndo) |
1368 | + } |
1369 | + Actions.Redo { |
1370 | + objectName: "RedoContextualAction" |
1371 | + enabled: contextModel && contextModel.isEditable && |
1372 | + (contextModel.editFlags & Oxide.WebView.RedoCapability) |
1373 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandRedo) |
1374 | + } |
1375 | + Actions.Cut { |
1376 | + objectName: "CutContextualAction" |
1377 | + enabled: contextModel && contextModel.isEditable && |
1378 | + (contextModel.editFlags & Oxide.WebView.CutCapability) |
1379 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandCut) |
1380 | + } |
1381 | + Actions.Copy { |
1382 | + objectName: "CopyContextualAction" |
1383 | + enabled: contextModel && contextModel.isEditable && |
1384 | + (contextModel.editFlags & Oxide.WebView.CopyCapability) |
1385 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandCopy) |
1386 | + } |
1387 | + Actions.Paste { |
1388 | + objectName: "PasteContextualAction" |
1389 | + enabled: contextModel && contextModel.isEditable && |
1390 | + (contextModel.editFlags & Oxide.WebView.PasteCapability) |
1391 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandPaste) |
1392 | + } |
1393 | + Actions.Erase { |
1394 | + objectName: "EraseContextualAction" |
1395 | + enabled: contextModel && contextModel.isEditable && |
1396 | + (contextModel.editFlags & Oxide.WebView.EraseCapability) |
1397 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandErase) |
1398 | + } |
1399 | + Actions.SelectAll { |
1400 | + objectName: "SelectAllContextualAction" |
1401 | + enabled: contextModel && contextModel.isEditable && |
1402 | + (contextModel.editFlags & Oxide.WebView.SelectAllCapability) |
1403 | + onTriggered: webviewimpl.executeEditingCommand(Oxide.WebView.EditingCommandSelectAll) |
1404 | + } |
1405 | + } |
1406 | + |
1407 | + Component { |
1408 | + id: contextMenuNarrowComponent |
1409 | + ContextMenuMobile { |
1410 | + actions: contextualActions |
1411 | + Component.onCompleted: webviewimpl.contextModel = contextModel |
1412 | + } |
1413 | + } |
1414 | + Component { |
1415 | + id: contextMenuWideComponent |
1416 | + ContextMenuWide { |
1417 | + webview: webviewimpl |
1418 | + parent: browser |
1419 | + actions: contextualActions |
1420 | + Component.onCompleted: webviewimpl.contextModel = contextModel |
1421 | + } |
1422 | + } |
1423 | + contextMenu: browser.wide ? contextMenuWideComponent : contextMenuNarrowComponent |
1424 | |
1425 | onNewViewRequested: { |
1426 | var tab = tabComponent.createObject(tabContainer, {"request": request, 'incognito': browser.incognito}) |
1427 | @@ -1089,12 +1172,6 @@ |
1428 | } |
1429 | } |
1430 | |
1431 | - Loader { |
1432 | - id: downloadLoader |
1433 | - source: formFactor == "desktop" ? "" : "../Downloader.qml" |
1434 | - asynchronous: true |
1435 | - } |
1436 | - |
1437 | QtObject { |
1438 | id: internal |
1439 | |
1440 | |
1441 | === added file 'src/app/webbrowser/ContextMenuMobile.qml' |
1442 | --- src/app/webbrowser/ContextMenuMobile.qml 1970-01-01 00:00:00 +0000 |
1443 | +++ src/app/webbrowser/ContextMenuMobile.qml 2015-08-27 14:01:06 +0000 |
1444 | @@ -0,0 +1,168 @@ |
1445 | +/* |
1446 | + * Copyright 2015 Canonical Ltd. |
1447 | + * |
1448 | + * This file is part of webbrowser-app. |
1449 | + * |
1450 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1451 | + * it under the terms of the GNU General Public License as published by |
1452 | + * the Free Software Foundation; version 3. |
1453 | + * |
1454 | + * webbrowser-app is distributed in the hope that it will be useful, |
1455 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1456 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1457 | + * GNU General Public License for more details. |
1458 | + * |
1459 | + * You should have received a copy of the GNU General Public License |
1460 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1461 | + */ |
1462 | + |
1463 | +import QtQuick 2.4 |
1464 | +import Ubuntu.Components 1.3 |
1465 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
1466 | +import Ubuntu.Components.Popups 1.3 as Popups |
1467 | +import com.canonical.Oxide 1.8 as Oxide |
1468 | + |
1469 | +Popups.Dialog { |
1470 | + property QtObject contextModel: model |
1471 | + property ActionList actions: null |
1472 | + |
1473 | + QtObject { |
1474 | + id: internal |
1475 | + readonly property bool isImage: ((contextModel.mediaType === Oxide.WebView.MediaTypeImage) || |
1476 | + (contextModel.mediaType === Oxide.WebView.MediaTypeCanvas)) && |
1477 | + contextModel.srcUrl.toString() |
1478 | + } |
1479 | + |
1480 | + Row { |
1481 | + spacing: units.gu(2) |
1482 | + anchors { |
1483 | + left: parent.left |
1484 | + leftMargin: units.gu(2) |
1485 | + right: parent.right |
1486 | + rightMargin: units.gu(2) |
1487 | + } |
1488 | + height: units.gu(2 * title.lineCount + 3) |
1489 | + visible: !contextModel.isEditable |
1490 | + |
1491 | + Icon { |
1492 | + width: units.gu(2) |
1493 | + height: units.gu(2) |
1494 | + anchors { |
1495 | + top: parent.top |
1496 | + topMargin: units.gu(2) |
1497 | + } |
1498 | + name: internal.isImage ? "stock_image" : "" |
1499 | + // work around the lack of a standard stock_link symbolic icon in the theme |
1500 | + Component.onCompleted: { |
1501 | + if (!name) { |
1502 | + source = "assets/stock_link.svg" |
1503 | + } |
1504 | + } |
1505 | + } |
1506 | + |
1507 | + Label { |
1508 | + id: title |
1509 | + objectName: "titleLabel" |
1510 | + text: internal.isImage ? contextModel.srcUrl : contextModel.linkUrl |
1511 | + width: parent.width - units.gu(4) |
1512 | + anchors { |
1513 | + top: parent.top |
1514 | + topMargin: units.gu(2) |
1515 | + bottom: parent.bottom |
1516 | + } |
1517 | + fontSize: "x-small" |
1518 | + maximumLineCount: 2 |
1519 | + wrapMode: Text.Wrap |
1520 | + height: contentHeight |
1521 | + } |
1522 | + } |
1523 | + |
1524 | + ListItems.ThinDivider { |
1525 | + anchors { |
1526 | + left: parent.left |
1527 | + leftMargin: units.gu(2) |
1528 | + right: parent.right |
1529 | + rightMargin: units.gu(2) |
1530 | + } |
1531 | + visible: !contextModel.isEditable |
1532 | + } |
1533 | + |
1534 | + Repeater { |
1535 | + model: actions.actions |
1536 | + delegate: ListItems.Empty { |
1537 | + readonly property var action: actions.actions[index] |
1538 | + objectName: action.objectName + "_item" |
1539 | + visible: action.enabled |
1540 | + showDivider: false |
1541 | + |
1542 | + height: units.gu(5) |
1543 | + |
1544 | + Label { |
1545 | + anchors { |
1546 | + left: parent.left |
1547 | + leftMargin: units.gu(2) |
1548 | + right: parent.right |
1549 | + rightMargin: units.gu(2) |
1550 | + verticalCenter: parent.verticalCenter |
1551 | + } |
1552 | + fontSize: "x-small" |
1553 | + text: action.text |
1554 | + } |
1555 | + |
1556 | + ListItems.ThinDivider { |
1557 | + anchors { |
1558 | + left: parent.left |
1559 | + leftMargin: units.gu(2) |
1560 | + right: parent.right |
1561 | + rightMargin: units.gu(2) |
1562 | + bottom: parent.bottom |
1563 | + } |
1564 | + } |
1565 | + |
1566 | + onTriggered: { |
1567 | + action.trigger() |
1568 | + contextModel.close() |
1569 | + } |
1570 | + } |
1571 | + } |
1572 | + |
1573 | + ListItems.Empty { |
1574 | + objectName: "cancelAction" |
1575 | + height: units.gu(5) |
1576 | + showDivider: false |
1577 | + Label { |
1578 | + anchors { |
1579 | + left: parent.left |
1580 | + leftMargin: units.gu(2) |
1581 | + right: parent.right |
1582 | + rightMargin: units.gu(2) |
1583 | + verticalCenter: parent.verticalCenter |
1584 | + } |
1585 | + fontSize: "x-small" |
1586 | + text: i18n.tr("Cancel") |
1587 | + } |
1588 | + onTriggered: contextModel.close() |
1589 | + } |
1590 | + |
1591 | + Component.onCompleted: { |
1592 | + if (contextModel.linkUrl.toString() || |
1593 | + contextModel.srcUrl.toString() || |
1594 | + (contextModel.isEditable && contextModel.editFlags)) { |
1595 | + show() |
1596 | + } else { |
1597 | + contextModel.close() |
1598 | + } |
1599 | + } |
1600 | + |
1601 | + // adjust default dialog visuals to custom requirements for the context menu |
1602 | + Binding { |
1603 | + target: __foreground |
1604 | + property: "margins" |
1605 | + value: 0 |
1606 | + } |
1607 | + Binding { |
1608 | + target: __foreground |
1609 | + property: "itemSpacing" |
1610 | + value: 0 |
1611 | + } |
1612 | +} |
1613 | |
1614 | === added file 'src/app/webbrowser/ContextMenuWide.qml' |
1615 | --- src/app/webbrowser/ContextMenuWide.qml 1970-01-01 00:00:00 +0000 |
1616 | +++ src/app/webbrowser/ContextMenuWide.qml 2015-08-27 14:01:06 +0000 |
1617 | @@ -0,0 +1,158 @@ |
1618 | +/* |
1619 | + * Copyright 2015 Canonical Ltd. |
1620 | + * |
1621 | + * This file is part of webbrowser-app. |
1622 | + * |
1623 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1624 | + * it under the terms of the GNU General Public License as published by |
1625 | + * the Free Software Foundation; version 3. |
1626 | + * |
1627 | + * webbrowser-app is distributed in the hope that it will be useful, |
1628 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1629 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1630 | + * GNU General Public License for more details. |
1631 | + * |
1632 | + * You should have received a copy of the GNU General Public License |
1633 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1634 | + */ |
1635 | + |
1636 | +import QtQuick 2.4 |
1637 | +import Ubuntu.Components 1.3 |
1638 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
1639 | +import Ubuntu.Components.Popups 1.3 as Popups |
1640 | +import com.canonical.Oxide 1.8 as Oxide |
1641 | + |
1642 | +Popups.Popover { |
1643 | + id: contextMenu |
1644 | + |
1645 | + property QtObject contextModel: model |
1646 | + property ActionList actions: null |
1647 | + property var webview: null |
1648 | + |
1649 | + QtObject { |
1650 | + id: internal |
1651 | + |
1652 | + readonly property bool isImage: ((contextModel.mediaType === Oxide.WebView.MediaTypeImage) || |
1653 | + (contextModel.mediaType === Oxide.WebView.MediaTypeCanvas)) && |
1654 | + contextModel.srcUrl.toString() |
1655 | + |
1656 | + readonly property int lastEnabledActionIndex: { |
1657 | + var last = -1 |
1658 | + for (var i in actions.actions) { |
1659 | + if (actions.actions[i].enabled) { |
1660 | + last = i |
1661 | + } |
1662 | + } |
1663 | + return last |
1664 | + } |
1665 | + |
1666 | + readonly property real locationBarOffset: contextMenu.webview.locationBarController.height + contextMenu.webview.locationBarController.offset |
1667 | + } |
1668 | + |
1669 | + Rectangle { |
1670 | + anchors.fill: parent |
1671 | + color: "#ececec" |
1672 | + } |
1673 | + |
1674 | + Column { |
1675 | + anchors { |
1676 | + left: parent.left |
1677 | + right: parent.right |
1678 | + } |
1679 | + |
1680 | + Label { |
1681 | + objectName: "titleLabel" |
1682 | + text: internal.isImage ? contextModel.srcUrl : contextModel.linkUrl |
1683 | + anchors { |
1684 | + left: parent.left |
1685 | + leftMargin: units.gu(2) |
1686 | + right: parent.right |
1687 | + rightMargin: units.gu(2) |
1688 | + } |
1689 | + height: units.gu(5) |
1690 | + visible: !contextModel.isEditable |
1691 | + fontSize: "x-small" |
1692 | + color: "#888888" |
1693 | + elide: Text.ElideRight |
1694 | + verticalAlignment: Text.AlignVCenter |
1695 | + } |
1696 | + |
1697 | + ListItems.ThinDivider { |
1698 | + anchors { |
1699 | + left: parent.left |
1700 | + leftMargin: units.gu(2) |
1701 | + right: parent.right |
1702 | + rightMargin: units.gu(2) |
1703 | + } |
1704 | + visible: !contextModel.isEditable |
1705 | + } |
1706 | + |
1707 | + Repeater { |
1708 | + model: actions.actions |
1709 | + delegate: ListItems.Empty { |
1710 | + readonly property var action: actions.actions[index] |
1711 | + objectName: action.objectName + "_item" |
1712 | + visible: action.enabled |
1713 | + showDivider: false |
1714 | + |
1715 | + height: units.gu(5) |
1716 | + |
1717 | + Label { |
1718 | + anchors { |
1719 | + left: parent.left |
1720 | + leftMargin: units.gu(2) |
1721 | + right: parent.right |
1722 | + rightMargin: units.gu(2) |
1723 | + verticalCenter: parent.verticalCenter |
1724 | + } |
1725 | + fontSize: "small" |
1726 | + text: action.text |
1727 | + } |
1728 | + |
1729 | + ListItems.ThinDivider { |
1730 | + visible: index < internal.lastEnabledActionIndex |
1731 | + anchors { |
1732 | + left: parent.left |
1733 | + leftMargin: units.gu(2) |
1734 | + right: parent.right |
1735 | + rightMargin: units.gu(2) |
1736 | + bottom: parent.bottom |
1737 | + } |
1738 | + } |
1739 | + |
1740 | + onTriggered: { |
1741 | + action.trigger() |
1742 | + contextMenu.hide() |
1743 | + } |
1744 | + } |
1745 | + } |
1746 | + } |
1747 | + |
1748 | + Item { |
1749 | + id: positioner |
1750 | + visible: false |
1751 | + parent: contextMenu.webview |
1752 | + // XXX: Because the context model’s position is incorrectly reported in |
1753 | + // device-independent pixels (see https://launchpad.net/bugs/1471181), |
1754 | + // it needs to be multiplied by the device pixel ratio to get physical pixels. |
1755 | + x: contextModel.position.x * contextMenu.webview.devicePixelRatio |
1756 | + y: contextModel.position.y * contextMenu.webview.devicePixelRatio + internal.locationBarOffset |
1757 | + } |
1758 | + caller: positioner |
1759 | + |
1760 | + Component.onCompleted: { |
1761 | + if (contextModel.linkUrl.toString() || |
1762 | + contextModel.srcUrl.toString() || |
1763 | + (contextModel.isEditable && contextModel.editFlags)) { |
1764 | + show() |
1765 | + } else { |
1766 | + contextModel.close() |
1767 | + } |
1768 | + } |
1769 | + |
1770 | + onVisibleChanged: { |
1771 | + if (!visible) { |
1772 | + contextModel.close() |
1773 | + } |
1774 | + } |
1775 | +} |
1776 | |
1777 | === added file 'src/app/webbrowser/assets/stock_link.svg' |
1778 | --- src/app/webbrowser/assets/stock_link.svg 1970-01-01 00:00:00 +0000 |
1779 | +++ src/app/webbrowser/assets/stock_link.svg 2015-08-27 14:01:06 +0000 |
1780 | @@ -0,0 +1,164 @@ |
1781 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
1782 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
1783 | + |
1784 | +<svg |
1785 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
1786 | + xmlns:cc="http://creativecommons.org/ns#" |
1787 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
1788 | + xmlns:svg="http://www.w3.org/2000/svg" |
1789 | + xmlns="http://www.w3.org/2000/svg" |
1790 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
1791 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
1792 | + width="90" |
1793 | + height="90" |
1794 | + id="svg4874" |
1795 | + version="1.1" |
1796 | + inkscape:version="0.48+devel r" |
1797 | + viewBox="0 0 90 90.000001" |
1798 | + sodipodi:docname="insert-link02b.svg"> |
1799 | + <defs |
1800 | + id="defs4876" /> |
1801 | + <sodipodi:namedview |
1802 | + id="base" |
1803 | + pagecolor="#ffffff" |
1804 | + bordercolor="#666666" |
1805 | + borderopacity="1.0" |
1806 | + inkscape:pageopacity="0.0" |
1807 | + inkscape:pageshadow="2" |
1808 | + inkscape:zoom="5.0931704" |
1809 | + inkscape:cx="51.019301" |
1810 | + inkscape:cy="39.641321" |
1811 | + inkscape:document-units="px" |
1812 | + inkscape:current-layer="g4978" |
1813 | + showgrid="true" |
1814 | + showborder="true" |
1815 | + fit-margin-top="0" |
1816 | + fit-margin-left="0" |
1817 | + fit-margin-right="0" |
1818 | + fit-margin-bottom="0" |
1819 | + inkscape:snap-bbox="true" |
1820 | + inkscape:bbox-paths="true" |
1821 | + inkscape:bbox-nodes="true" |
1822 | + inkscape:snap-bbox-edge-midpoints="true" |
1823 | + inkscape:snap-bbox-midpoints="true" |
1824 | + inkscape:object-paths="true" |
1825 | + inkscape:snap-intersection-paths="true" |
1826 | + inkscape:object-nodes="true" |
1827 | + inkscape:snap-smooth-nodes="true" |
1828 | + inkscape:snap-midpoints="true" |
1829 | + inkscape:snap-object-midpoints="true" |
1830 | + inkscape:snap-center="true" |
1831 | + showguides="true" |
1832 | + inkscape:guide-bbox="true"> |
1833 | + <inkscape:grid |
1834 | + type="xygrid" |
1835 | + id="grid5451" |
1836 | + empspacing="6" /> |
1837 | + <sodipodi:guide |
1838 | + orientation="1,0" |
1839 | + position="6,77" |
1840 | + id="guide4063" /> |
1841 | + <sodipodi:guide |
1842 | + orientation="1,0" |
1843 | + position="3,78" |
1844 | + id="guide4065" /> |
1845 | + <sodipodi:guide |
1846 | + orientation="0,1" |
1847 | + position="55,84" |
1848 | + id="guide4067" /> |
1849 | + <sodipodi:guide |
1850 | + orientation="0,1" |
1851 | + position="53,87" |
1852 | + id="guide4069" /> |
1853 | + <sodipodi:guide |
1854 | + orientation="0,1" |
1855 | + position="20,3" |
1856 | + id="guide4071" /> |
1857 | + <sodipodi:guide |
1858 | + orientation="0,1" |
1859 | + position="20,6" |
1860 | + id="guide4073" /> |
1861 | + <sodipodi:guide |
1862 | + orientation="1,0" |
1863 | + position="87,7" |
1864 | + id="guide4075" /> |
1865 | + <sodipodi:guide |
1866 | + orientation="1,0" |
1867 | + position="84,7" |
1868 | + id="guide4077" /> |
1869 | + <sodipodi:guide |
1870 | + orientation="0,1" |
1871 | + position="58,81" |
1872 | + id="guide4074" /> |
1873 | + <sodipodi:guide |
1874 | + orientation="1,0" |
1875 | + position="9,74" |
1876 | + id="guide4076" /> |
1877 | + <sodipodi:guide |
1878 | + orientation="0,1" |
1879 | + position="21,9" |
1880 | + id="guide4078" /> |
1881 | + <sodipodi:guide |
1882 | + orientation="1,0" |
1883 | + position="81,4" |
1884 | + id="guide4080" /> |
1885 | + </sodipodi:namedview> |
1886 | + <metadata |
1887 | + id="metadata4879"> |
1888 | + <rdf:RDF> |
1889 | + <cc:Work |
1890 | + rdf:about=""> |
1891 | + <dc:format>image/svg+xml</dc:format> |
1892 | + <dc:type |
1893 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
1894 | + <dc:title></dc:title> |
1895 | + </cc:Work> |
1896 | + </rdf:RDF> |
1897 | + </metadata> |
1898 | + <g |
1899 | + inkscape:label="Layer 1" |
1900 | + inkscape:groupmode="layer" |
1901 | + id="layer1" |
1902 | + transform="translate(67.857146,-84.50504)"> |
1903 | + <g |
1904 | + transform="matrix(0,-1,-1,0,373.50506,516.50504)" |
1905 | + id="g4845" |
1906 | + style="display:inline"> |
1907 | + <g |
1908 | + inkscape:label="Layer 1" |
1909 | + id="layer1-8" |
1910 | + transform="matrix(0,-1,-1,0,1394.3622,441.36221)"> |
1911 | + <rect |
1912 | + style="color:#000000;fill:none;stroke:none;stroke-width:7.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1913 | + id="rect4198" |
1914 | + width="90" |
1915 | + height="90" |
1916 | + x="0" |
1917 | + y="962.36218" /> |
1918 | + <g |
1919 | + id="g4978" |
1920 | + transform="translate(-60,548.00002)"> |
1921 | + <rect |
1922 | + style="color:#000000;fill:none;stroke:none;stroke-width:6;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1923 | + id="rect3038" |
1924 | + width="90" |
1925 | + height="90" |
1926 | + x="0" |
1927 | + y="1.7382814e-05" |
1928 | + transform="translate(60,414.36216)" /> |
1929 | + <path |
1930 | + 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" |
1931 | + d="M 92.075086,472.28711 117.92492,446.43727" |
1932 | + id="path4188" |
1933 | + inkscape:connector-curvature="0" |
1934 | + sodipodi:nodetypes="cc" /> |
1935 | + <path |
1936 | + 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" |
1937 | + 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 " |
1938 | + transform="translate(60.000003,414.36218)" |
1939 | + id="path4190" /> |
1940 | + </g> |
1941 | + </g> |
1942 | + </g> |
1943 | + </g> |
1944 | +</svg> |
1945 | |
1946 | === modified file 'src/app/webcontainer/WebViewImplOxide.qml' |
1947 | --- src/app/webcontainer/WebViewImplOxide.qml 2015-08-10 15:33:43 +0000 |
1948 | +++ src/app/webcontainer/WebViewImplOxide.qml 2015-08-27 14:01:06 +0000 |
1949 | @@ -18,7 +18,7 @@ |
1950 | |
1951 | import QtQuick 2.4 |
1952 | import QtQuick.Window 2.2 |
1953 | -import com.canonical.Oxide 1.3 as Oxide |
1954 | +import com.canonical.Oxide 1.8 as Oxide |
1955 | import Ubuntu.Components 1.3 |
1956 | import Ubuntu.Components.Popups 1.3 |
1957 | import Ubuntu.UnityWebApps 0.1 as UnityWebApps |
1958 | @@ -68,12 +68,49 @@ |
1959 | |
1960 | contextualActions: ActionList { |
1961 | Actions.CopyLink { |
1962 | - enabled: webview.contextualData.href.toString() |
1963 | - onTriggered: Clipboard.push(["text/plain", webview.contextualData.href.toString()]) |
1964 | + enabled: webview.contextModel && webview.contextModel.linkUrl.toString() |
1965 | + onTriggered: Clipboard.push(["text/plain", contextModel.linkUrl.toString()]) |
1966 | } |
1967 | Actions.CopyImage { |
1968 | - enabled: webview.contextualData.img.toString() |
1969 | - onTriggered: Clipboard.push(["text/plain", webview.contextualData.img.toString()]) |
1970 | + enabled: webview.contextModel && |
1971 | + (webview.contextModel.mediaType === Oxide.WebView.MediaTypeImage) && |
1972 | + webview.contextModel.srcUrl.toString() |
1973 | + onTriggered: Clipboard.push(["text/plain", contextModel.srcUrl.toString()]) |
1974 | + } |
1975 | + Actions.Undo { |
1976 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
1977 | + (webview.contextModel.editFlags & Oxide.WebView.UndoCapability) |
1978 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandUndo) |
1979 | + } |
1980 | + Actions.Redo { |
1981 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
1982 | + (webview.contextModel.editFlags & Oxide.WebView.RedoCapability) |
1983 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandRedo) |
1984 | + } |
1985 | + Actions.Cut { |
1986 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
1987 | + (webview.contextModel.editFlags & Oxide.WebView.CutCapability) |
1988 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandCut) |
1989 | + } |
1990 | + Actions.Copy { |
1991 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
1992 | + (webview.contextModel.editFlags & Oxide.WebView.CopyCapability) |
1993 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandCopy) |
1994 | + } |
1995 | + Actions.Paste { |
1996 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
1997 | + (webview.contextModel.editFlags & Oxide.WebView.PasteCapability) |
1998 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandPaste) |
1999 | + } |
2000 | + Actions.Erase { |
2001 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
2002 | + (webview.contextModel.editFlags & Oxide.WebView.EraseCapability) |
2003 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandErase) |
2004 | + } |
2005 | + Actions.SelectAll { |
2006 | + enabled: webview.contextModel && webview.contextModel.isEditable && |
2007 | + (webview.contextModel.editFlags & Oxide.WebView.SelectAllCapability) |
2008 | + onTriggered: webview.executeEditingCommand(Oxide.WebView.EditingCommandSelectAll) |
2009 | } |
2010 | } |
2011 | |
2012 | |
2013 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' |
2014 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2015-08-12 19:03:44 +0000 |
2015 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-08-27 14:01:06 +0000 |
2016 | @@ -115,13 +115,6 @@ |
2017 | def get_geolocation_dialog(self): |
2018 | return self.wait_select_single(GeolocationPermissionRequest) |
2019 | |
2020 | - def get_selection(self): |
2021 | - return self.wait_select_single(Selection) |
2022 | - |
2023 | - def get_selection_actions(self): |
2024 | - return self.wait_select_single("ActionSelectionPopover", |
2025 | - objectName="selectionActions") |
2026 | - |
2027 | def get_tabs_view(self): |
2028 | return self.wait_select_single(TabsList, visible=True) |
2029 | |
2030 | @@ -175,6 +168,12 @@ |
2031 | def press_key(self, key): |
2032 | self.keyboard.press_and_release(key) |
2033 | |
2034 | + def get_context_menu(self): |
2035 | + if self.wide: |
2036 | + return self.wait_select_single(ContextMenuWide) |
2037 | + else: |
2038 | + return self.wait_select_single(ContextMenuMobile) |
2039 | + |
2040 | |
2041 | class Chrome(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2042 | |
2043 | @@ -324,15 +323,6 @@ |
2044 | return self.select_single("Button", objectName="allow") |
2045 | |
2046 | |
2047 | -class Selection(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2048 | - |
2049 | - def get_rectangle(self): |
2050 | - return self.select_single("QQuickItem", objectName="rectangle") |
2051 | - |
2052 | - def get_handle(self, name): |
2053 | - return self.select_single("SelectionHandle", objectName=name) |
2054 | - |
2055 | - |
2056 | class TabPreview(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2057 | |
2058 | @autopilot.logging.log_action(logger.info) |
2059 | @@ -574,3 +564,30 @@ |
2060 | def get_header_from_folder(self, folder): |
2061 | return folder.wait_select_single("QQuickItem", |
2062 | objectName="bookmarkFolderHeader") |
2063 | + |
2064 | + |
2065 | +class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2066 | + |
2067 | + def get_title_label(self): |
2068 | + return self.select_single("Label", objectName="titleLabel") |
2069 | + |
2070 | + def get_visible_actions(self): |
2071 | + return self.select_many("Empty", visible=True) |
2072 | + |
2073 | + def click_action(self, objectName): |
2074 | + name = objectName + "_item" |
2075 | + action = self.select_single("Empty", visible=True, |
2076 | + enabled=True, objectName=name) |
2077 | + self.pointing_device.click_object(action) |
2078 | + |
2079 | + |
2080 | +class ContextMenuWide(ContextMenuBase): |
2081 | + |
2082 | + pass |
2083 | + |
2084 | + |
2085 | +class ContextMenuMobile(ContextMenuBase): |
2086 | + |
2087 | + def click_cancel_action(self): |
2088 | + action = self.select_single("Empty", objectName="cancelAction") |
2089 | + self.pointing_device.click_object(action) |
2090 | |
2091 | === modified file 'tests/autopilot/webbrowser_app/tests/http_server.py' |
2092 | --- tests/autopilot/webbrowser_app/tests/http_server.py 2015-07-02 10:03:20 +0000 |
2093 | +++ tests/autopilot/webbrowser_app/tests/http_server.py 2015-08-27 14:01:06 +0000 |
2094 | @@ -20,8 +20,14 @@ |
2095 | """ |
2096 | A custom HTTP request handler that serves GET resources. |
2097 | """ |
2098 | + |
2099 | suggestions_data = {} |
2100 | |
2101 | + base64_png_data = \ |
2102 | + "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAACXBIWXMAAAsTAAALEwE" \ |
2103 | + "AmpwYAAAAOUlEQVRYw+3OAQ0AAAgDoGv/zlpDN0hATS7qaGlpaWlpaWlpaWlpaWlpaW" \ |
2104 | + "lpaWlpaWlpaWlpab1qLUGqAWNyFWTYAAAAAElFTkSuQmCC" |
2105 | + |
2106 | def make_html(self, title, body): |
2107 | html = "<html><title>{}</title><body>{}</body></html>" |
2108 | return html.format(title, body) |
2109 | @@ -81,6 +87,28 @@ |
2110 | html += 'src="/blanktargetlink" />' |
2111 | html += '</body></html>' |
2112 | self.send_html(html) |
2113 | + elif self.path == "/image": |
2114 | + self.send_response(200) |
2115 | + html = '<html><body>' |
2116 | + html += '<img src="data:image/png;base64,' + self.base64_png_data |
2117 | + html += '" style="position: fixed; top: 50%; left: 50%; ' |
2118 | + html += 'transform: translate(-50%, -50%)" />' |
2119 | + html += '</body></html>' |
2120 | + self.send_html(html) |
2121 | + elif self.path == "/imagelink": |
2122 | + self.send_response(200) |
2123 | + html = '<html><body><a href="/test1">' |
2124 | + html += '<img src="data:image/png;base64,' + self.base64_png_data |
2125 | + html += '" style="position: fixed; top: 50%; left: 50%; ' |
2126 | + html += 'transform: translate(-50%, -50%)" />' |
2127 | + html += '</a></body></html>' |
2128 | + self.send_html(html) |
2129 | + elif self.path == "/textarea": |
2130 | + self.send_response(200) |
2131 | + html = '<html><body style="margin: 0">' |
2132 | + html += '<textarea style="width: 100%; height: 100%">some text' |
2133 | + html += '</textarea></body></html>' |
2134 | + self.send_html(html) |
2135 | elif self.path == "/uploadform": |
2136 | # craft a page that accepts clicks anywhere inside its window |
2137 | # and on a click opens up the content picker. |
2138 | |
2139 | === added file 'tests/autopilot/webbrowser_app/tests/test_contextmenu.py' |
2140 | --- tests/autopilot/webbrowser_app/tests/test_contextmenu.py 1970-01-01 00:00:00 +0000 |
2141 | +++ tests/autopilot/webbrowser_app/tests/test_contextmenu.py 2015-08-27 14:01:06 +0000 |
2142 | @@ -0,0 +1,177 @@ |
2143 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2144 | +# |
2145 | +# Copyright 2015 Canonical |
2146 | +# |
2147 | +# This program is free software: you can redistribute it and/or modify it |
2148 | +# under the terms of the GNU General Public License version 3, as published |
2149 | +# by the Free Software Foundation. |
2150 | +# |
2151 | +# This program is distributed in the hope that it will be useful, |
2152 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2153 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2154 | +# GNU General Public License for more details. |
2155 | +# |
2156 | +# You should have received a copy of the GNU General Public License |
2157 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2158 | + |
2159 | +import time |
2160 | +from autopilot.platform import model |
2161 | +from autopilot.matchers import Eventually |
2162 | +import testtools |
2163 | +from testtools.matchers import Equals, GreaterThan, StartsWith |
2164 | + |
2165 | +from webbrowser_app.tests import StartOpenRemotePageTestCaseBase |
2166 | + |
2167 | + |
2168 | +class TestContextMenuBase(StartOpenRemotePageTestCaseBase): |
2169 | + |
2170 | + data_uri_prefix = "data:image/png;base64," |
2171 | + |
2172 | + def setUp(self, path): |
2173 | + super(TestContextMenuBase, self).setUp(path) |
2174 | + self.menu = self.open_context_menu() |
2175 | + |
2176 | + def open_context_menu(self): |
2177 | + webview = self.main_window.get_current_webview() |
2178 | + chrome = self.main_window.chrome |
2179 | + x = webview.globalRect.x + webview.globalRect.width // 2 |
2180 | + y = webview.globalRect.y + \ |
2181 | + (webview.globalRect.height + chrome.height) // 2 |
2182 | + self.pointing_device.move(x, y) |
2183 | + if model() == 'Desktop': |
2184 | + self.pointing_device.click(button=3) |
2185 | + else: |
2186 | + self.pointing_device.press() |
2187 | + time.sleep(1.5) |
2188 | + self.pointing_device.release() |
2189 | + return self.main_window.get_context_menu() |
2190 | + |
2191 | + def click_action(self, name): |
2192 | + self.menu.click_action(name) |
2193 | + self.menu.wait_until_destroyed() |
2194 | + |
2195 | + def verify_link_opened_in_a_new_tab(self): |
2196 | + self.assert_number_webviews_eventually(2) |
2197 | + webview = self.main_window.get_current_webview() |
2198 | + new_url = self.base_url + "/test1" |
2199 | + self.assertThat(webview.url, Eventually(Equals(new_url))) |
2200 | + |
2201 | + def verify_link_bookmarked(self): |
2202 | + url = self.base_url + "/test1" |
2203 | + self.main_window.go_to_url(url) |
2204 | + self.main_window.wait_until_page_loaded(url) |
2205 | + self.main_window.chrome.bookmarked.wait_for(True) |
2206 | + |
2207 | + def verify_image_opened_in_a_new_tab(self): |
2208 | + self.assert_number_webviews_eventually(2) |
2209 | + webview = self.main_window.get_current_webview() |
2210 | + self.assertThat(webview.url, |
2211 | + Eventually(StartsWith(self.data_uri_prefix))) |
2212 | + |
2213 | + |
2214 | +class TestContextMenuLink(TestContextMenuBase): |
2215 | + |
2216 | + def setUp(self): |
2217 | + super(TestContextMenuLink, self).setUp(path="/link") |
2218 | + self.assertThat(self.menu.get_title_label().text, |
2219 | + Equals(self.base_url + "/test1")) |
2220 | + |
2221 | + def test_dismiss_menu(self): |
2222 | + if self.main_window.wide: |
2223 | + # Verify that clicking outside the menu dismisses it |
2224 | + webview_rect = self.main_window.get_current_webview().globalRect |
2225 | + actions = self.menu.get_visible_actions() |
2226 | + self.assertThat(actions[0].globalRect.x, |
2227 | + GreaterThan(webview_rect.x)) |
2228 | + outside_x = (webview_rect.x + actions[0].globalRect.x) // 2 |
2229 | + outside_y = webview_rect.y + webview_rect.height // 2 |
2230 | + self.pointing_device.move(outside_x, outside_y) |
2231 | + self.pointing_device.click() |
2232 | + else: |
2233 | + # Verify that clicking the cancel action dismisses it |
2234 | + self.menu.click_cancel_action() |
2235 | + self.menu.wait_until_destroyed() |
2236 | + |
2237 | + def test_open_link_in_new_tab(self): |
2238 | + self.click_action("openLinkInNewTabContextualAction") |
2239 | + self.verify_link_opened_in_a_new_tab() |
2240 | + |
2241 | + def test_bookmark_link(self): |
2242 | + self.click_action("bookmarkLinkContextualAction") |
2243 | + self.verify_link_bookmarked() |
2244 | + |
2245 | + def test_copy_link(self): |
2246 | + # There is no easy way to test the contents of the clipboard, |
2247 | + # but we can at least verify that the context menu was dismissed. |
2248 | + self.click_action("copyLinkContextualAction") |
2249 | + |
2250 | + @testtools.skipIf(model() == "Desktop", "on devices only") |
2251 | + def test_share_link(self): |
2252 | + self.click_action("ShareLinkContextualAction") |
2253 | + self.main_window.wait_select_single("ContentShareDialog") |
2254 | + |
2255 | + |
2256 | +class TestContextMenuImage(TestContextMenuBase): |
2257 | + |
2258 | + def setUp(self): |
2259 | + super(TestContextMenuImage, self).setUp(path="/image") |
2260 | + self.assertThat(self.menu.get_title_label().text, |
2261 | + StartsWith(self.data_uri_prefix)) |
2262 | + |
2263 | + def test_open_image_in_new_tab(self): |
2264 | + self.click_action("OpenImageInNewTabContextualAction") |
2265 | + self.verify_image_opened_in_a_new_tab() |
2266 | + |
2267 | + def test_copy_image(self): |
2268 | + # There is no easy way to test the contents of the clipboard, |
2269 | + # but we can at least verify that the context menu was dismissed. |
2270 | + self.click_action("CopyImageContextualAction") |
2271 | + |
2272 | + |
2273 | +class TestContextMenuImageAndLink(TestContextMenuBase): |
2274 | + |
2275 | + def setUp(self): |
2276 | + super(TestContextMenuImageAndLink, self).setUp(path="/imagelink") |
2277 | + self.assertThat(self.menu.get_title_label().text, |
2278 | + StartsWith(self.data_uri_prefix)) |
2279 | + |
2280 | + def test_open_link_in_new_tab(self): |
2281 | + self.click_action("openLinkInNewTabContextualAction") |
2282 | + self.verify_link_opened_in_a_new_tab() |
2283 | + |
2284 | + def test_bookmark_link(self): |
2285 | + self.click_action("bookmarkLinkContextualAction") |
2286 | + self.verify_link_bookmarked() |
2287 | + |
2288 | + def test_copy_link(self): |
2289 | + # There is no easy way to test the contents of the clipboard, |
2290 | + # but we can at least verify that the context menu was dismissed. |
2291 | + self.click_action("copyLinkContextualAction") |
2292 | + |
2293 | + @testtools.skipIf(model() == "Desktop", "on devices only") |
2294 | + def test_share_link(self): |
2295 | + self.click_action("ShareLinkContextualAction") |
2296 | + self.main_window.wait_select_single("ContentShareDialog") |
2297 | + |
2298 | + def test_open_image_in_new_tab(self): |
2299 | + self.click_action("OpenImageInNewTabContextualAction") |
2300 | + self.verify_image_opened_in_a_new_tab() |
2301 | + |
2302 | + def test_copy_image(self): |
2303 | + # There is no easy way to test the contents of the clipboard, |
2304 | + # but we can at least verify that the context menu was dismissed. |
2305 | + self.click_action("CopyImageContextualAction") |
2306 | + |
2307 | + |
2308 | +class TestContextMenuTextArea(TestContextMenuBase): |
2309 | + |
2310 | + def setUp(self): |
2311 | + super(TestContextMenuTextArea, self).setUp(path="/textarea") |
2312 | + self.assertThat(self.menu.get_title_label().visible, Equals(False)) |
2313 | + |
2314 | + def test_actions(self): |
2315 | + actions = ["SelectAll", "Cut", "Undo", "Redo", |
2316 | + "Paste", "SelectAll", "Copy", "Erase"] |
2317 | + for action in actions: |
2318 | + self.click_action("{}ContextualAction".format(action)) |
2319 | + self.menu = self.open_context_menu() |
2320 | |
2321 | === removed file 'tests/autopilot/webbrowser_app/tests/test_selection.py' |
2322 | --- tests/autopilot/webbrowser_app/tests/test_selection.py 2015-07-03 16:04:21 +0000 |
2323 | +++ tests/autopilot/webbrowser_app/tests/test_selection.py 1970-01-01 00:00:00 +0000 |
2324 | @@ -1,92 +0,0 @@ |
2325 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2326 | -# |
2327 | -# Copyright 2014-2015 Canonical |
2328 | -# |
2329 | -# This program is free software: you can redistribute it and/or modify it |
2330 | -# under the terms of the GNU General Public License version 3, as published |
2331 | -# by the Free Software Foundation. |
2332 | -# |
2333 | -# This program is distributed in the hope that it will be useful, |
2334 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2335 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2336 | -# GNU General Public License for more details. |
2337 | -# |
2338 | -# You should have received a copy of the GNU General Public License |
2339 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2340 | - |
2341 | -import time |
2342 | -from autopilot.platform import model |
2343 | -from autopilot.matchers import Eventually |
2344 | -from testtools.matchers import Equals, GreaterThan, LessThan |
2345 | - |
2346 | -from webbrowser_app.tests import StartOpenRemotePageTestCaseBase |
2347 | - |
2348 | - |
2349 | -class TestSelection(StartOpenRemotePageTestCaseBase): |
2350 | - |
2351 | - def setUp(self): |
2352 | - super(TestSelection, self).setUp(path="/selection") |
2353 | - webview = self.main_window.get_current_webview() |
2354 | - self.pointing_device.move_to_object(webview) |
2355 | - if model() == 'Desktop': |
2356 | - self.pointing_device.click(button=3) |
2357 | - else: |
2358 | - self.pointing_device.press() |
2359 | - time.sleep(1.5) |
2360 | - self.pointing_device.release() |
2361 | - self.selection = self.main_window.get_selection() |
2362 | - self.rectangle = self.selection.get_rectangle() |
2363 | - self.assertThat(self.rectangle.width, LessThan(webview.width)) |
2364 | - self.assertThat(self.rectangle.height, LessThan(webview.height)) |
2365 | - self.actions = self.main_window.get_selection_actions() |
2366 | - self.assertThat(len(self.actions.select_many("Empty")), Equals(1)) |
2367 | - |
2368 | - def assert_selection_eventually_dismissed(self): |
2369 | - self.actions.wait_until_destroyed() |
2370 | - self.selection.wait_until_destroyed() |
2371 | - |
2372 | - def test_copy_selection(self): |
2373 | - copy_action = self.actions.select_single("Empty") |
2374 | - self.pointing_device.click_object(copy_action) |
2375 | - self.assert_selection_eventually_dismissed() |
2376 | - |
2377 | - def test_cancel_selection(self): |
2378 | - webview = self.main_window.get_current_webview() |
2379 | - x = int((webview.globalRect.x + self.rectangle.globalRect.x) / 2) |
2380 | - y = int(webview.globalRect.y + webview.globalRect.height / 2) |
2381 | - self.pointing_device.move(x, y) |
2382 | - self.pointing_device.click() |
2383 | - self.assert_selection_eventually_dismissed() |
2384 | - |
2385 | - def test_resize_selection(self): |
2386 | - webview = self.main_window.get_current_webview() |
2387 | - rect = self.rectangle.globalRect |
2388 | - |
2389 | - # Grow selection to the right |
2390 | - handle = self.selection.get_handle("rightHandle") |
2391 | - x0 = handle.globalRect.x + int(handle.globalRect.width / 2) |
2392 | - y0 = handle.globalRect.y + int(handle.globalRect.height / 2) |
2393 | - x1 = int((x0 + webview.globalRect.x + webview.globalRect.width) / 2) |
2394 | - y1 = y0 |
2395 | - self.pointing_device.drag(x0, y0, x1, y1) |
2396 | - self.assertThat(self.rectangle.width, |
2397 | - Eventually(GreaterThan(rect.width))) |
2398 | - self.assertThat(self.rectangle.height, |
2399 | - Eventually(GreaterThan(rect.height))) |
2400 | - self.actions.wait_until_destroyed() |
2401 | - self.actions = self.main_window.get_selection_actions() |
2402 | - |
2403 | - # Shrink selection from the bottom |
2404 | - handle = self.selection.get_handle("bottomHandle") |
2405 | - x0 = handle.globalRect.x + int(handle.globalRect.width / 2) |
2406 | - y0 = handle.globalRect.y + int(handle.globalRect.height / 2) |
2407 | - x1 = x0 |
2408 | - y1 = webview.globalRect.y + int(webview.globalRect.height * 0.6) |
2409 | - self.pointing_device.drag(x0, y0, x1, y1) |
2410 | - self.assertThat(self.rectangle.globalRect, Eventually(Equals(rect))) |
2411 | - self.actions.wait_until_destroyed() |
2412 | - self.actions = self.main_window.get_selection_actions() |
2413 | - |
2414 | - def test_navigating_discards_selection(self): |
2415 | - self.main_window.go_to_url(self.base_url + "/test1") |
2416 | - self.assert_selection_eventually_dismissed() |
2417 | |
2418 | === added file 'tests/unittests/qml/tst_FileExtensionMapper.qml' |
2419 | --- tests/unittests/qml/tst_FileExtensionMapper.qml 1970-01-01 00:00:00 +0000 |
2420 | +++ tests/unittests/qml/tst_FileExtensionMapper.qml 2015-08-27 14:01:06 +0000 |
2421 | @@ -0,0 +1,39 @@ |
2422 | +/* |
2423 | + * Copyright 2015 Canonical Ltd. |
2424 | + * |
2425 | + * This file is part of webbrowser-app. |
2426 | + * |
2427 | + * webbrowser-app is free software; you can redistribute it and/or modify |
2428 | + * it under the terms of the GNU General Public License as published by |
2429 | + * the Free Software Foundation; version 3. |
2430 | + * |
2431 | + * webbrowser-app is distributed in the hope that it will be useful, |
2432 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2433 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2434 | + * GNU General Public License for more details. |
2435 | + * |
2436 | + * You should have received a copy of the GNU General Public License |
2437 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2438 | + */ |
2439 | + |
2440 | +import QtTest 1.0 |
2441 | +import "../../../src/app/FileExtensionMapper.js" as FileExtensionMapper |
2442 | + |
2443 | +TestCase { |
2444 | + name: "FileExtensionMapper" |
2445 | + |
2446 | + function test_getExtension_data() { |
2447 | + return [ |
2448 | + {in: "", ext: ""}, |
2449 | + {in: "pdf", ext: ""}, |
2450 | + {in: ".vimrc", ext: ""}, |
2451 | + {in: "example.pdf", ext: "pdf"}, |
2452 | + {in: "http://example.org/path/example.pdf", ext: "pdf"}, |
2453 | + {in: "EXAMPLE.PDF", ext: "pdf"} |
2454 | + ] |
2455 | + } |
2456 | + |
2457 | + function test_getExtension(data) { |
2458 | + compare(FileExtensionMapper.getExtension(data.in), data.ext) |
2459 | + } |
2460 | +} |
2461 | |
2462 | === modified file 'tests/unittests/qml/tst_UbuntuWebView02.qml' |
2463 | --- tests/unittests/qml/tst_UbuntuWebView02.qml 2015-08-10 15:22:00 +0000 |
2464 | +++ tests/unittests/qml/tst_UbuntuWebView02.qml 2015-08-27 14:01:06 +0000 |
2465 | @@ -18,20 +18,101 @@ |
2466 | |
2467 | import QtQuick 2.4 |
2468 | import QtTest 1.0 |
2469 | +import Ubuntu.Test 1.0 |
2470 | +import Ubuntu.Components 1.3 |
2471 | import Ubuntu.Web 0.2 |
2472 | |
2473 | -TestCase { |
2474 | - name: "WebView" |
2475 | - |
2476 | - function test_context_singleton() { |
2477 | - compare(webview1.context, webview2.context) |
2478 | - } |
2479 | - |
2480 | - WebView { |
2481 | - id: webview1 |
2482 | - } |
2483 | - |
2484 | - WebView { |
2485 | - id: webview2 |
2486 | +Item { |
2487 | + id: root |
2488 | + |
2489 | + width: 200 |
2490 | + height: 200 |
2491 | + |
2492 | + Component { |
2493 | + id: webviewComponent |
2494 | + WebView { |
2495 | + anchors.fill: parent |
2496 | + } |
2497 | + } |
2498 | + |
2499 | + ActionList { |
2500 | + id: actionList |
2501 | + Action { |
2502 | + id: action1 |
2503 | + } |
2504 | + Action { |
2505 | + id: action2 |
2506 | + } |
2507 | + } |
2508 | + |
2509 | + readonly property string htmlWithHyperlink: '<html><body style="margin: 0"><a href="http://example.org/"><div style="height: 100%"></div></a></body></html>' |
2510 | + |
2511 | + UbuntuTestCase { |
2512 | + name: "UbuntuWebView02" |
2513 | + when: windowShown |
2514 | + |
2515 | + property var webview: null |
2516 | + |
2517 | + function init() { |
2518 | + webview = webviewComponent.createObject(root) |
2519 | + } |
2520 | + |
2521 | + function cleanup() { |
2522 | + webview.destroy() |
2523 | + } |
2524 | + |
2525 | + function test_context_singleton() { |
2526 | + var other = webviewComponent.createObject(root) |
2527 | + compare(other.context, webview.context) |
2528 | + other.destroy() |
2529 | + } |
2530 | + |
2531 | + function rightClickWebview() { |
2532 | + var center = centerOf(webview) |
2533 | + mouseClick(webview, center.x, center.y, Qt.RightButton) |
2534 | + // give the context menu a chance to appear before carrying on |
2535 | + wait(500) |
2536 | + } |
2537 | + |
2538 | + function getContextMenu() { |
2539 | + return findChild(webview, "contextMenu") |
2540 | + } |
2541 | + |
2542 | + function dismissContextMenu() { |
2543 | + var center = centerOf(webview) |
2544 | + mouseClick(webview, center.x, center.y) |
2545 | + wait(500) |
2546 | + compare(getContextMenu(), null) |
2547 | + } |
2548 | + |
2549 | + function test_no_contextual_actions() { |
2550 | + webview.loadHtml(root.htmlWithHyperlink, "file:///") |
2551 | + tryCompare(webview, "loading", false) |
2552 | + rightClickWebview() |
2553 | + compare(getContextMenu(), null) |
2554 | + } |
2555 | + |
2556 | + function test_contextual_actions() { |
2557 | + webview.contextualActions = actionList |
2558 | + webview.loadHtml(root.htmlWithHyperlink, "file:///") |
2559 | + tryCompare(webview, "loading", false) |
2560 | + rightClickWebview() |
2561 | + compare(getContextMenu().actions, actionList) |
2562 | + compare(webview.contextualData.href, "http://example.org/") |
2563 | + dismissContextMenu() |
2564 | + compare(webview.contextualData.href, "") |
2565 | + } |
2566 | + |
2567 | + function test_contextual_actions_all_disabled() { |
2568 | + webview.contextualActions = actionList |
2569 | + action1.enabled = false |
2570 | + action2.enabled = false |
2571 | + webview.loadHtml(root.htmlWithHyperlink, "file:///") |
2572 | + tryCompare(webview, "loading", false) |
2573 | + rightClickWebview() |
2574 | + compare(getContextMenu(), null) |
2575 | + action1.enabled = true |
2576 | + action2.enabled = true |
2577 | + } |
2578 | } |
2579 | } |
FAILED: Continuous integration, rev:1128 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 2130/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 3828 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 884 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 884 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 884/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 884 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-mako/ 3145 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3825 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3825/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 22758
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: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 2130/rebuild
http://