Merge lp:~abreu-alexandre/unity-webapps-qml/handle-bidirectional-callback-params into lp:unity-webapps-qml

Proposed by Alexandre Abreu
Status: Merged
Approved by: Robert Bruce Park
Approved revision: 129
Merged at revision: 127
Proposed branch: lp:~abreu-alexandre/unity-webapps-qml/handle-bidirectional-callback-params
Merge into: lp:unity-webapps-qml
Diff against target: 939 lines (+473/-56)
19 files modified
debian/rules (+1/-1)
debian/unity-webapps-qml-autopilot.install (+3/-3)
src/Ubuntu/UnityWebApps/Settings.qml (+1/-1)
src/Ubuntu/UnityWebApps/UnityWebApps.js (+38/-4)
src/Ubuntu/UnityWebApps/UnityWebApps.pro (+0/-3)
src/Ubuntu/UnityWebApps/UnityWebApps.qml (+50/-21)
src/Ubuntu/UnityWebApps/UnityWebAppsUtils.js (+68/-0)
src/Ubuntu/UnityWebApps/common/js/unity-binding-bridge.js (+63/-0)
tests/autopilot/autopilot.pro (+19/-1)
tests/autopilot/html/test_webapps_callback_dispatch.html (+57/-0)
tests/autopilot/html/test_webapps_callback_dispatch_api.js.in (+58/-0)
tests/autopilot/qml/FullWebViewApp.qml (+20/-1)
tests/autopilot/qml/test_webapps_callback_dispatch_api.qml (+14/-0)
tests/autopilot/unity_webapps_qml/tests/__init__.py (+9/-5)
tests/autopilot/unity_webapps_qml/tests/test_callbackDispatch.py (+60/-0)
tests/integration/integration.pro (+0/-3)
tests/tests.pro (+1/-1)
tests/unit/test_qml/tst_dispatch.qml (+4/-4)
tools/qml-launcher/qml-launcher.cpp (+7/-8)
To merge this branch: bzr merge lp:~abreu-alexandre/unity-webapps-qml/handle-bidirectional-callback-params
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
WebApps Pending
Review via email: mp+226502@code.launchpad.net

Commit message

Add bidirectional callback support between js <-> qml,

Description of the change

Add bidirectional callback support between js <-> qml,

The current situation is that javascript can (and has to) handoff js callbacks to qml, so that events are properly propagated and translated. One use case recently came up that highlighted the limitation that basically the "callback translation between js & qml" does not work both ways. The use case is for content hub share handler that might ask js to call a qml callback when an operation is done.

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

handle bidirectional callback parameters betwee js & qml

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

What is the use case?

I added a couple of inline comments.

128. By Alexandre Abreu

fixes

129. By Alexandre Abreu

fix nit

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/rules'
2--- debian/rules 2013-07-11 17:53:01 +0000
3+++ debian/rules 2014-07-18 17:03:51 +0000
4@@ -9,7 +9,7 @@
5
6 override_dh_install:
7 # install autopilot tests
8- cd tests/integration/autopilot; \
9+ cd tests/autopilot; \
10 set -ex; for python in $(shell pyversions -r); do \
11 $$python setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; \
12 done; \
13
14=== modified file 'debian/unity-webapps-qml-autopilot.install'
15--- debian/unity-webapps-qml-autopilot.install 2014-07-08 19:30:06 +0000
16+++ debian/unity-webapps-qml-autopilot.install 2014-07-18 17:03:51 +0000
17@@ -1,4 +1,4 @@
18-tests/integration/autopilot/html/* usr/share/unity-webapps-qml/autopilot-tests/html/
19-tests/integration/autopilot/data/* usr/share/unity-webapps-qml/autopilot-tests/data/
20-tests/integration/autopilot/qml/* usr/share/unity-webapps-qml/autopilot-tests/qml/
21+tests/autopilot/html/* usr/share/unity-webapps-qml/autopilot-tests/html/
22+tests/autopilot/data/* usr/share/unity-webapps-qml/autopilot-tests/data/
23+tests/autopilot/qml/* usr/share/unity-webapps-qml/autopilot-tests/qml/
24 usr/lib/python*
25
26=== modified file 'src/Ubuntu/UnityWebApps/Settings.qml'
27--- src/Ubuntu/UnityWebApps/Settings.qml 2014-06-20 13:32:55 +0000
28+++ src/Ubuntu/UnityWebApps/Settings.qml 2014-07-18 17:03:51 +0000
29@@ -23,6 +23,6 @@
30 *
31 */
32 property bool injectExtraUbuntuApis: false
33- property bool injectExtraUILaunchCapabilities: false
34+ property bool injectExtraContentShareCapabilities: false
35 property bool requiresInit: true
36 }
37
38=== modified file 'src/Ubuntu/UnityWebApps/UnityWebApps.js'
39--- src/Ubuntu/UnityWebApps/UnityWebApps.js 2014-06-20 13:32:55 +0000
40+++ src/Ubuntu/UnityWebApps/UnityWebApps.js 2014-07-18 17:03:51 +0000
41@@ -36,12 +36,13 @@
42 * \param backends
43 * \param userscriptContent
44 */
45- function _UnityWebApps(parentItem, bindeeProxies, accessPolicy) {
46- this._injected_unity_api_path = Qt.resolvedUrl('unity-webapps-api.js');
47+ function _UnityWebApps(parentItem, bindeeProxies, accessPolicy, injected_api_path) {
48+ this._injected_unity_api_path = injected_api_path;
49 this._bindeeProxies = bindeeProxies;
50 this._backends = null;
51 this._userscripts = [];
52 this._accessPolicy = accessPolicy;
53+ this._callbackManager = UnityWebAppsUtils.makeCallbackManager();
54
55 this._bind();
56 };
57@@ -110,7 +111,7 @@
58 *
59 */
60 _onMessage: function(msg) {
61- if ( ! this._isValidWebAppsMessage(msg)) {
62+ if ( ! this._isValidWebAppsMessage(msg) && ! this._isValidCallbackMessage(msg)) {
63 this._log ('Invalid message received: ' + json.stringify(msg));
64
65 return;
66@@ -129,6 +130,7 @@
67 return true;
68 },
69
70+
71 /**
72 * \internal
73 *
74@@ -175,6 +177,23 @@
75 objectid: objectid,
76 class_name: class_name,
77 method_name: method_name}]);
78+
79+ } else if (target === UnityWebAppsUtils.UBUNTU_WEBAPPS_BINDING_API_CALLBACK_MESSAGE) {
80+
81+ var id = message.id;
82+
83+ if (! id || ! params)
84+ return;
85+
86+ var cbfunc = this._callbackManager.get(id);
87+ if (!cbfunc || !(cbfunc instanceof Function)) {
88+ try {
89+ console.log('Invalid callback id: ' + id);
90+ }
91+ catch (e) {}
92+ return;
93+ }
94+ cbfunc.apply(null, params);
95 }
96 },
97
98@@ -215,6 +234,10 @@
99 return;
100
101 var callback_args = Array.prototype.slice.call(arguments);
102+ callback_args = callback_args.map (function (arg) {
103+ return UnityWebAppsUtils.transformCallbacksToIds(arg, self._callbackManager);
104+ });
105+
106 var message = UnityWebAppsUtils.formatUnityWebappsCallbackCall(callbackid, callback_args);
107
108 self._bindeeProxies.sendToPage(JSON.stringify(message));
109@@ -258,7 +281,7 @@
110 }
111 }
112 return ret;
113- },
114+ },
115
116 /**
117 * \internal
118@@ -281,6 +304,17 @@
119 message.target.indexOf('ubuntu-webapps-binding-call') === 0 &&
120 message.name &&
121 message.args;
122+ },
123+
124+ /**
125+ * \internal
126+ *
127+ */
128+ _isValidCallbackMessage: function(message) {
129+ return message != null &&
130+ message.target &&
131+ message.target.indexOf('ubuntu-webapps-binding-callback-call') === 0 &&
132+ message.args;
133 }
134 };
135
136
137=== modified file 'src/Ubuntu/UnityWebApps/UnityWebApps.pro'
138--- src/Ubuntu/UnityWebApps/UnityWebApps.pro 2014-05-16 16:29:35 +0000
139+++ src/Ubuntu/UnityWebApps/UnityWebApps.pro 2014-07-18 17:03:51 +0000
140@@ -4,9 +4,6 @@
141 TEMPLATE = subdirs
142 SUBDIRS += plugin
143
144-#
145-#
146-#
147 UNITY_API_JS_FILE = $$system($$PWD/../../../tools/inject-js-utils.py unity-webapps-api.js.in unity-webapps-api.js)
148
149 inject_dependancies.target = unity-webapps-api.js
150
151=== modified file 'src/Ubuntu/UnityWebApps/UnityWebApps.qml'
152--- src/Ubuntu/UnityWebApps/UnityWebApps.qml 2014-07-08 19:27:21 +0000
153+++ src/Ubuntu/UnityWebApps/UnityWebApps.qml 2014-07-18 17:03:51 +0000
154@@ -28,7 +28,6 @@
155 import "./bindings/online-accounts/backend/online-accounts.js" as OnlineAccountsApiBackend
156 import "./bindings/download-manager/backend/download-api.js" as DownloadApiBackend
157
158-
159 /*!
160 \qmltype UnityWebApps
161 \inqmlmodule Ubuntu.UnityWebApps 0.1
162@@ -121,10 +120,10 @@
163 property alias injectExtraUbuntuApis: settings.injectExtraUbuntuApis
164
165 /*!
166- \qmlproperty bool UnityWebApps::injectExtraUILaunchCapabilities
167+ \qmlproperty bool UnityWebApps::injectExtraContentShareCapabilities
168
169 */
170- property alias injectExtraUILaunchCapabilities: settings.injectExtraUILaunchCapabilities
171+ property alias injectExtraContentShareCapabilities: settings.injectExtraContentShareCapabilities
172
173 /*!
174 \qmlproperty bool UnityWebApps::requiresInit
175@@ -142,13 +141,22 @@
176 property var actionsContext: null
177
178 /*!
179- \qmlproperty string UnityWebApps::_opt_backendProxies
180+ \qmlproperty string UnityWebApps::customBackendProxies
181
182 Used only for testing.
183 Allows optional (not the default ones) mocked backends to be used.
184
185 */
186- property var _opt_backendProxies: null
187+ property var customBackendProxies: null
188+
189+ /*!
190+ \qmlproperty string UnityWebApps::_opt_clientApiFileUrl
191+
192+ Used only for testing.
193+ Allows optional (not the default ones) client api to be used instead of the default one.
194+
195+ */
196+ property string customClientApiFileUrl: ""
197
198 /*!
199 \qmlproperty string UnityWebApps::_opt_homepage
200@@ -162,7 +170,7 @@
201
202 Settings {
203 id: settings
204- injectExtraUILaunchCapabilities: name && name.length && name.length !== 0
205+ injectExtraContentShareCapabilities: name && name.length && name.length !== 0
206 }
207
208 /*
209@@ -216,10 +224,15 @@
210 console.debug('__bind: ERROR bindee proxies not valid')
211 return;
212 }
213+ var instance =
214+ new UnityWebAppsJs.UnityWebApps(
215+ webapps,
216+ bindeeProxies,
217+ __getPolicyForContent(settings),
218+ customClientApiFileUrl && customClientApiFileUrl.length !== 0
219+ ? customClientApiFileUrl
220+ : Qt.resolvedUrl('unity-webapps-api.js'));
221
222- var instance = new UnityWebAppsJs.UnityWebApps(webapps,
223- bindeeProxies,
224- __getPolicyForContent(settings));
225 internal.instance = instance;
226
227 if (internal.backends)
228@@ -232,8 +245,8 @@
229 */
230 function __createBackendsIfNeeded() {
231 var backends;
232- if (_opt_backendProxies != null)
233- backends = _opt_backendProxies;
234+ if (customBackendProxies != null)
235+ backends = customBackendProxies;
236 else {
237 backends = __makeBackendProxies();
238 }
239@@ -245,7 +258,7 @@
240
241 */
242 function __initBackends() {
243- if (__isValidWebAppName(webapps.name) || injectExtraUbuntuApis) {
244+ if (customBackendProxies || __isValidWebAppName(webapps.name) || injectExtraUbuntuApis) {
245 internal.backends = __createBackendsIfNeeded();
246 if (internal.backends && internal.instance)
247 internal.instance.setBackends(internal.backends);
248@@ -368,6 +381,8 @@
249 }
250 }
251
252+ onCustomBackendProxiesChanged: __initBackends()
253+
254 /*!
255 \internal
256
257@@ -448,17 +463,32 @@
258 this.restrictions = restrictions || BASIC_WEBAPPS_ALLOWED_APIS;
259 };
260 RestrictedPolicy.prototype.allowed = function(signature) {
261- return this.restrictions.some(function(e) { return e === signature; });
262+ return this.restrictions.some(function(e) { return signature.match(e); });
263+ };
264+ RestrictedPolicy.prototype.add = function(signature) {
265+ if (! this.restrictions.some(function(e) { return e === signature; })) {
266+ this.restrictions.push(signature);
267+ }
268 };
269
270 if (settings.injectExtraUbuntuApis)
271 return new PassthroughPolicy();
272
273-// if (settings.injectExtraUILaunchCapabilities)
274-// return new RestrictedPolicy(["launchEmbeddedUI", "ContentHub.onShareRequested"]);
275-
276- // Inject only the basic init + api
277- return new PassthroughPolicy()
278+ var policy = new RestrictedPolicy(['init',
279+ 'addAction',
280+ 'clearAction',
281+ 'clearActions',
282+ 'acceptData',
283+ 'Launcher.*',
284+ 'Notification.*',
285+ 'Launcher.*',
286+ 'MediaPlayer.*',
287+ 'MessagingIndicator.*']);
288+ if (settings.injectExtraContentShareCapabilities) {
289+ policy.add("launchEmbeddedUI");
290+ policy.add("ContentHub.onShareRequested");
291+ }
292+ return policy;
293 }
294
295 /*!
296@@ -602,11 +632,10 @@
297 uiobject.destroy();
298 return;
299 }
300- function _onCompleted(data) {
301+ function _onCompleted(data, onResourceUploadedCallback) {
302 p.visible = true;
303 uiobject.onCompleted.disconnect(_onCompleted);
304- uiobject.destroy();
305- callback(data);
306+ callback(data, onResourceUploadedCallback);
307 }
308 uiobject.onCompleted.connect(_onCompleted);
309 };
310
311=== modified file 'src/Ubuntu/UnityWebApps/UnityWebAppsUtils.js'
312--- src/Ubuntu/UnityWebApps/UnityWebAppsUtils.js 2014-07-08 19:27:21 +0000
313+++ src/Ubuntu/UnityWebApps/UnityWebAppsUtils.js 2014-07-18 17:03:51 +0000
314@@ -19,6 +19,7 @@
315 .pragma library
316
317 var UBUNTU_WEBAPPS_BINDING_API_CALL_MESSAGE = "ubuntu-webapps-binding-call";
318+var UBUNTU_WEBAPPS_BINDING_API_CALLBACK_MESSAGE = "ubuntu-webapps-binding-callback-call";
319 var UBUNTU_WEBAPPS_BINDING_OBJECT_METHOD_CALL_MESSAGE = "ubuntu-webapps-binding-call-object-method";
320
321
322@@ -331,4 +332,71 @@
323 + pad(d.getUTCSeconds()) + 'Z';
324 };
325
326+function transformFunctionToCallbackIdIfNecessary(obj, callbackManager) {
327+ var ret = obj;
328+ if (obj instanceof Function && callbackManager && callbackManager.store) {
329+ var id = callbackManager.store(obj);
330+ ret = {callbackid: id};
331+ }
332+ return ret;
333+}
334+
335+function transformCallbacksToIds(obj, callbackManager) {
336+ if ( ! isIterableObject(obj)) {
337+ return transformFunctionToCallbackIdIfNecessary(obj, callbackManager);
338+ }
339+ var ret = (obj instanceof Array) ? [] : {};
340+ for (var key in obj) {
341+ if (obj.hasOwnProperty(key)) {
342+ if (obj[key] instanceof Function) {
343+ ret[key] = transformFunctionToCallbackIdIfNecessary(obj[key], callbackManager);
344+ }
345+ else if (isIterableObject (obj[key])) {
346+ ret[key] = transformCallbacksToIds(obj[key]);
347+ }
348+ else {
349+ ret[key] = obj[key];
350+ }
351+ }
352+ }
353+ return ret;
354+}
355+
356+/**
357+ * Wraps callback ids in proper callback that dispatch to the
358+ * webpage thru a proper event
359+ *
360+ */
361+function wrapCallbackIds(obj) {
362+ if ( ! obj)
363+ return obj;
364+
365+ if (!UnityWebAppsUtils.isIterableObject(obj)) {
366+ return obj;
367+ }
368+
369+ if (obj
370+ && obj.hasOwnProperty('callbackid')
371+ && obj.callbackid !== null) {
372+ return this._makeWebpageCallback (obj.callbackid);
373+ }
374+
375+ var ret = (obj instanceof Array) ? [] : {};
376+ for (var key in obj) {
377+ if (obj.hasOwnProperty(key)) {
378+ if (UnityWebAppsUtils.isIterableObject (obj[key])) {
379+ if (obj[key].callbackid !== null) {
380+ ret[key] = this._makeWebpageCallback (obj[key].callbackid);
381+ }
382+ else {
383+ ret[key] = this._wrapCallbackIds (obj[key]);
384+ }
385+ }
386+ else {
387+ ret[key] = obj[key];
388+ }
389+ }
390+ }
391+ return ret;
392+}
393
394
395=== modified file 'src/Ubuntu/UnityWebApps/common/js/unity-binding-bridge.js'
396--- src/Ubuntu/UnityWebApps/common/js/unity-binding-bridge.js 2014-03-18 18:52:11 +0000
397+++ src/Ubuntu/UnityWebApps/common/js/unity-binding-bridge.js 2014-07-18 17:03:51 +0000
398@@ -137,6 +137,64 @@
399 },
400
401 /**
402+ * \internal
403+ *
404+ */
405+ _makeWebpageCallback: function (callbackid) {
406+ var self = this;
407+ return function () {
408+ var callback_args = Array.prototype.slice.call(arguments);
409+
410+ callback_args = callback_args.map (function (arg) {
411+ return self._transformCallbacksToIds(arg);
412+ });
413+
414+ var message = formatUnityWebappsCallbackCall(callbackid, JSON.stringify(callback_args));
415+
416+ self._sendToBackend(JSON.stringify(message));
417+ };
418+ },
419+
420+ /**
421+ * \internal
422+ *
423+ * Wraps callback ids in proper callback that dispatch to the
424+ * webpage thru a proper event
425+ *
426+ */
427+ _wrapCallbackIds: function (obj) {
428+ if ( ! obj)
429+ return obj;
430+ if ( ! isIterableObject(obj)) {
431+ return obj;
432+ }
433+
434+ if (obj
435+ && obj.hasOwnProperty('callbackid')
436+ && obj.callbackid !== null) {
437+ return this._makeWebpageCallback (obj.callbackid);
438+ }
439+
440+ var ret = (obj instanceof Array) ? [] : {};
441+ for (var key in obj) {
442+ if (obj.hasOwnProperty(key)) {
443+ if (UnityWebAppsUtils.isIterableObject (obj[key])) {
444+ if (obj[key].callbackid != null) {
445+ ret[key] = this._makeWebpageCallback (obj[key].callbackid);
446+ }
447+ else {
448+ ret[key] = this._wrapCallbackIds (obj[key]);
449+ }
450+ }
451+ else {
452+ ret[key] = obj[key];
453+ }
454+ }
455+ }
456+ return ret;
457+ },
458+
459+ /**
460 * @internal
461 */
462 _dispatchCallbackCall: function(id, args) {
463@@ -174,6 +232,11 @@
464 else if (arg instanceof Array) {
465 return self._translateArgs(arg);
466 }
467+ else if (arg
468+ && arg.hasOwnProperty('callbackid')
469+ && arg.callbackid !== null) {
470+ return self._makeWebpageCallback (arg.callbackid);
471+ }
472
473 return arg;
474 });
475
476=== renamed directory 'tests/integration/autopilot' => 'tests/autopilot'
477=== modified file 'tests/autopilot/autopilot.pro'
478--- tests/integration/autopilot/autopilot.pro 2014-07-08 19:27:21 +0000
479+++ tests/autopilot/autopilot.pro 2014-07-18 17:03:51 +0000
480@@ -1,5 +1,16 @@
481 TEMPLATE=aux
482
483+UNITY_API_JS_FILE = $$system($$PWD/../../tools/inject-js-utils.py ./html/test_webapps_callback_dispatch_api.js.in ./html/test_webapps_callback_dispatch_api.js)
484+
485+inject_dependancies.target = ./html/test_webapps_callback_dispatch_api.js
486+inject_dependancies.depends = ./html/test_webapps_callback_dispatch_api.js.in
487+inject_dependancies.commands = $$PWD/../../tools/inject-js-utils.py $< $@
488+
489+QMAKE_EXTRA_TARGETS += inject_dependancies
490+
491+PRE_TARGETDEPS += \
492+ ./html/test_webapps_callback_dispatch_api.js
493+
494 OTHER_FILES += \
495 $$system(ls ./qml/*) \
496 $$system(ls ./html/*) \
497@@ -11,4 +22,11 @@
498 unity_webapps_qml/tests/fake_servers.py \
499 qml/WebviewBackendWebkit.qml \
500 qml/WebviewBackendOxide.qml \
501- qml/message-server.js
502+ qml/message-server.js \
503+ $$system(ls ./data/installed-webapps/*) \
504+ unity_webapps_qml/tests/test_callbackDispatch.py \
505+ html/test_webapps_callback_dispatch.html \
506+ html/test_webapps_callback_dispatch_api.js \
507+ html/test_webapps_callback_dispatch_api.js.in \
508+ qml/test_webapps_callback_dispatch_api.qml
509+
510
511=== added file 'tests/autopilot/html/test_webapps_callback_dispatch.html'
512--- tests/autopilot/html/test_webapps_callback_dispatch.html 1970-01-01 00:00:00 +0000
513+++ tests/autopilot/html/test_webapps_callback_dispatch.html 2014-07-18 17:03:51 +0000
514@@ -0,0 +1,57 @@
515+<html>
516+
517+<head>
518+<title>Unity Webapps QML test: callback</title>
519+
520+<script>
521+
522+window.onload = function () {
523+ var unity;
524+ function init() {
525+ unity = window.external.getUnityObject('1.0');
526+ doCallbackLoop();
527+ }
528+
529+ var CALLBACK_COUNT_MAX_COUNT = 10;
530+ var callbackLoopCount = 0;
531+ function doCallbackLoop(callback) {
532+ if (callbackLoopCount >= CALLBACK_COUNT_MAX_COUNT) {
533+ document.getElementById('content').innerHTML = 'callback-loop-count-reached';
534+ return;
535+ }
536+ ++callbackLoopCount;
537+
538+ if (callback && typeof callback === 'Function') {
539+ callback(doCallbackLoop);
540+ }
541+ else {
542+ unity.withCallback(doCallbackLoop);
543+ }
544+ }
545+
546+ init();
547+
548+ if (window.external.getUnityObject) {
549+ init();
550+ }
551+ else {
552+ document.addEventListener('ubuntu-webapps-api-ready', function () {
553+ init();
554+ }, false);
555+ }
556+}
557+
558+</script>
559+
560+</head>
561+
562+<body>
563+
564+HELLO WORLD
565+
566+<div id="content">
567+</div>
568+
569+</body>
570+
571+</html>
572
573=== added file 'tests/autopilot/html/test_webapps_callback_dispatch_api.js.in'
574--- tests/autopilot/html/test_webapps_callback_dispatch_api.js.in 1970-01-01 00:00:00 +0000
575+++ tests/autopilot/html/test_webapps_callback_dispatch_api.js.in 2014-07-18 17:03:51 +0000
576@@ -0,0 +1,58 @@
577+/*
578+ * Copyright 2014 Canonical Ltd.
579+ *
580+ * This file is part of unity-webapps-qml.
581+ *
582+ * unity-webapps-qml is free software; you can redistribute it and/or modify
583+ * it under the terms of the GNU General Public License as published by
584+ * the Free Software Foundation; version 3.
585+ *
586+ * unity-webapps-qml is distributed in the hope that it will be useful,
587+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
588+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
589+ * GNU General Public License for more details.
590+ *
591+ * You should have received a copy of the GNU General Public License
592+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
593+ */
594+
595+(function () {
596+
597+ // Acknowledge that the API has been fully injected
598+ var sendApiCreatedAcknowledgeEvent = function () {
599+ var e = document.createEvent ("Events");
600+ e.initEvent ("ubuntu-webapps-api-ready", false, false);
601+ document.dispatchEvent (e);
602+ };
603+
604+ //@include ../../src/Ubuntu/UnityWebApps/UnityWebAppsUtils.js
605+ //@include ../../src/Ubuntu/UnityWebApps/common/js/unity-backend-messaging-proxy.js
606+ //@include ../../src/Ubuntu/UnityWebApps/common/js/unity-binding-proxy.js
607+ //@include ../../src/Ubuntu/UnityWebApps/common/js/unity-binding-bridge.js
608+
609+ var apiBuilder = function(backend) {
610+ return {
611+ withCallback: function(callback) {
612+ backend.call('withCallback', [callback]);
613+ }
614+ };
615+ };
616+
617+ var apiBridge = new UnityBindingBridge(
618+ makeCallbackManager(),
619+ createMessagingProxyForCurrentWebRuntime());
620+
621+ var api = apiBuilder (apiBridge);
622+
623+ apiBridge.setBindingApi(api);
624+
625+ if (!window.external)
626+ window.external = {};
627+
628+ window.external.getUnityObject = function (version) {
629+ return api;
630+ };
631+
632+ sendApiCreatedAcknowledgeEvent();
633+}) ();
634+
635
636=== modified file 'tests/autopilot/qml/FullWebViewApp.qml'
637--- tests/integration/autopilot/qml/FullWebViewApp.qml 2014-07-08 19:27:21 +0000
638+++ tests/autopilot/qml/FullWebViewApp.qml 2014-07-18 17:03:51 +0000
639@@ -51,6 +51,10 @@
640
641 property bool useOxide: false
642 property string url: ""
643+
644+ property string apiBackendQmlFileUrl: ""
645+ property string clientApiFileUrl: ""
646+
647 property string webappName: ""
648 property string webappSearchPath: ""
649 property string webappHomepage: ""
650@@ -82,9 +86,18 @@
651 }
652
653 Loader {
654+ id: apiBackendQmlFileLoader
655+ source: apiBackendQmlFileUrl.length !== 0 ? apiBackendQmlFileUrl : ""
656+ }
657+
658+ Loader {
659 id: unityWebappsComponentLoader
660 anchors.fill: parent
661- sourceComponent: webView !== null && webappName.length !== 0 ? unityWebappsComponent : null
662+ sourceComponent: (webView !== null && webappName.length !== 0) ?
663+ (apiBackendQmlFileUrl.length !== 0 ?
664+ (apiBackendQmlFileLoader.item ? unityWebappsComponent : undefined)
665+ : unityWebappsComponent)
666+ : null
667 }
668
669 UnityWebappsAppModel {
670@@ -100,9 +113,15 @@
671 objectName: "webappsContainer"
672 actionsContext: webappsActionsContext
673 name: root.webappName
674+ injectExtraUbuntuApis: true
675+ customBackendProxies: apiBackendQmlFileLoader.item
676+ ? apiBackendQmlFileLoader.item.buildapi()
677+ : undefined
678+ customClientApiFileUrl: root.clientApiFileUrl
679 bindee: webView
680 _opt_homepage: root.webappHomepage
681 model: webappModel
682 }
683 }
684 }
685+
686
687=== added file 'tests/autopilot/qml/test_webapps_callback_dispatch_api.qml'
688--- tests/autopilot/qml/test_webapps_callback_dispatch_api.qml 1970-01-01 00:00:00 +0000
689+++ tests/autopilot/qml/test_webapps_callback_dispatch_api.qml 2014-07-18 17:03:51 +0000
690@@ -0,0 +1,14 @@
691+import QtQuick 2.0
692+
693+Item {
694+ function buildapi() {
695+ return {
696+ withCallback: function(clientCallback) {
697+ function callbackLoop(callback) {
698+ callback(callbackLoop);
699+ }
700+ clientCallback(callbackLoop);
701+ }
702+ };
703+ }
704+}
705
706=== modified file 'tests/autopilot/unity_webapps_qml/tests/__init__.py'
707--- tests/integration/autopilot/unity_webapps_qml/tests/__init__.py 2014-07-08 19:27:21 +0000
708+++ tests/autopilot/unity_webapps_qml/tests/__init__.py 2014-07-18 17:03:51 +0000
709@@ -21,7 +21,7 @@
710
711 from unity.tests import UnityTestCase
712
713-LOCAL_QML_LAUNCHER_APP_PATH = "%s/%s" % (os.path.dirname(os.path.realpath(__file__)), '../../../../../tools/qml-launcher/unity-webapps-qml-launcher')
714+LOCAL_QML_LAUNCHER_APP_PATH = "%s/%s" % (os.path.dirname(os.path.realpath(__file__)), '../../../../tools/qml-launcher/unity-webapps-qml-launcher')
715 INSTALLED_QML_LAUNCHER_APP_PATH = 'unity-webapps-qml-launcher'
716
717 # TODO create __init__.py.in
718@@ -31,6 +31,7 @@
719 BASE_URL = ''
720
721 class UnityWebappsTestCaseBase(UnityTestCase):
722+
723 def setUp(self):
724 super(UnityWebappsTestCaseBase, self).setUp()
725 self.use_oxide = False
726@@ -56,7 +57,8 @@
727 webapp_name='unitywebappsqmllauncher',
728 webapp_search_path="",
729 webapp_homepage="",
730- use_oxide=False):
731+ use_oxide=False,
732+ extra_params=[]):
733 base_params = ['--qml=' + self.get_qml_browser_container_path(),
734 '--app-id=' + webapp_name,
735 '--webappName=' + webapp_name,
736@@ -74,15 +76,17 @@
737 if os.path.exists(LOCAL_QML_LAUNCHER_APP_PATH):
738 # we are local
739 base_params.append('--import=' + os.path.join (os.path.dirname(os.path.realpath(__file__)),
740- '../../../../../src'))
741+ '../../../../src'))
742+
743+ base_params += extra_params
744
745 return base_params
746
747- def launch_with_html_filepath(self, html_filepath):
748+ def launch_with_html_filepath(self, html_filepath, extra_params=[]):
749 self.assertThat(os.path.exists(html_filepath), Equals(True))
750 url = self.create_file_url(html_filepath)
751
752- self.launch_application(self.get_launch_params(url))
753+ self.launch_application(self.get_launch_params(url, 'unitywebappsqmllauncher', '', '', False, extra_params))
754 self.assert_url_eventually_loaded(url)
755
756 def launch_application(self, args):
757
758=== added file 'tests/autopilot/unity_webapps_qml/tests/test_callbackDispatch.py'
759--- tests/autopilot/unity_webapps_qml/tests/test_callbackDispatch.py 1970-01-01 00:00:00 +0000
760+++ tests/autopilot/unity_webapps_qml/tests/test_callbackDispatch.py 2014-07-18 17:03:51 +0000
761@@ -0,0 +1,60 @@
762+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
763+# Copyright 2014 Canonical
764+#
765+# This program is free software: you can redistribute it and/or modify it
766+# under the terms of the GNU General Public License version 3, as published
767+# by the Free Software Foundation.
768+
769+from __future__ import absolute_import
770+
771+import time
772+import os
773+
774+from testtools.matchers import Equals, GreaterThan, NotEquals
775+from autopilot.matchers import Eventually
776+
777+from unity_webapps_qml.tests import UnityWebappsTestCaseBase
778+
779+LOCAL_HTML_TEST_FILE = "%s/%s" % (os.path.dirname(os.path.realpath(__file__)), '../../html/test_webapps_callback_dispatch.html')
780+INSTALLED_HTML_TEST_FILE = '/usr/share/unity-webapps-qml/autopilot-tests/html/test_webapps_callback_dispatch.html'
781+
782+LOCAL_JS_TEST_FILE = "%s/%s" % (os.path.dirname(os.path.realpath(__file__)), '../../html/test_webapps_callback_dispatch_api.js')
783+INSTALLED_JS_TEST_FILE = '/usr/share/unity-webapps-qml/autopilot-tests/html/test_webapps_callback_dispatch_api.js'
784+
785+LOCAL_QML_BACKEND_TEST_FILE = "%s/%s" % (os.path.dirname(os.path.realpath(__file__)), '../../qml/test_webapps_callback_dispatch_api.qml')
786+INSTALLED_QML_BACKEND_TEST_FILE = '/usr/share/unity-webapps-qml/autopilot-tests/qml/test_webapps_callback_dispatch_api.qml'
787+
788+class WebappsCallbackDispatchTestCaseBase(UnityWebappsTestCaseBase):
789+ def setUp(self):
790+ super(WebappsCallbackDispatchTestCaseBase, self).setUp()
791+ self.launch_with_html_filepath(
792+ self.get_html_test_file(),
793+ ['--clientApiFileUrl=file://' + self.get_js_test_file(),
794+ '--apiBackendQmlFileUrl=file://' + self.get_qml_test_file()])
795+
796+ def get_html_test_file(self):
797+ if os.path.exists(LOCAL_HTML_TEST_FILE):
798+ return os.path.abspath(LOCAL_HTML_TEST_FILE)
799+ return INSTALLED_HTML_TEST_FILE
800+
801+ def get_js_test_file(self):
802+ if os.path.exists(LOCAL_JS_TEST_FILE):
803+ return os.path.abspath(LOCAL_JS_TEST_FILE)
804+ return INSTALLED_JS_TEST_FILE
805+
806+ def get_qml_test_file(self):
807+ print LOCAL_QML_BACKEND_TEST_FILE
808+ if os.path.exists(LOCAL_QML_BACKEND_TEST_FILE):
809+ return os.path.abspath(LOCAL_QML_BACKEND_TEST_FILE)
810+ return INSTALLED_QML_BACKEND_TEST_FILE
811+
812+ def test_bidirectionalCallback(self):
813+ self.assertThat(
814+ lambda: self.eval_expression_in_page_unsafe(
815+ 'return window.external.getUnityObject("1.0") != null;'),
816+ Eventually(Equals(True)))
817+
818+ self.assertThat(
819+ lambda: self.eval_expression_in_page_unsafe("return document.getElementById('content').innerHTML;"),
820+ Eventually(Equals('callback-loop-count-reached')))
821+
822
823=== removed directory 'tests/integration'
824=== removed file 'tests/integration/integration.pro'
825--- tests/integration/integration.pro 2013-07-09 19:02:23 +0000
826+++ tests/integration/integration.pro 1970-01-01 00:00:00 +0000
827@@ -1,3 +0,0 @@
828-TEMPLATE = subdirs
829-
830-SUBDIRS = autopilot
831
832=== modified file 'tests/tests.pro'
833--- tests/tests.pro 2013-07-24 20:31:38 +0000
834+++ tests/tests.pro 2014-07-18 17:03:51 +0000
835@@ -1,4 +1,4 @@
836 TEMPLATE=subdirs
837
838-SUBDIRS = unit integration
839+SUBDIRS = unit autopilot
840
841
842=== modified file 'tests/unit/test_qml/tst_dispatch.qml'
843--- tests/unit/test_qml/tst_dispatch.qml 2014-04-14 20:57:17 +0000
844+++ tests/unit/test_qml/tst_dispatch.qml 2014-07-18 17:03:51 +0000
845@@ -19,7 +19,7 @@
846
847 var simple_backend = {This: { Is: { A: {Backend: function (args) { mockedWebView.called(args) } } } } };
848
849- webapps._opt_backendProxies = simple_backend;
850+ webapps.customBackendProxies = simple_backend;
851 webapps.name = "test_properBackendDispatched";
852 webapps.bindee = mockedWebView;
853
854@@ -47,7 +47,7 @@
855
856 var simple_backend = {This: { Is: { A: {Backend: action } } } };
857
858- webapps._opt_backendProxies = simple_backend;
859+ webapps.customBackendProxies = simple_backend;
860 webapps.name = "test_backendDispatchedWithProperArguments";
861 webapps.bindee = mockedWebView;
862
863@@ -65,7 +65,7 @@
864
865 var invalid_backend = {This: { Is: { Not: { A: {Backend: function (args) { mockedWebView.called(args) } } } } } };
866
867- webapps._opt_backendProxies = invalid_backend;
868+ webapps.customBackendProxies = invalid_backend;
869 webapps.name = "test_invalidBackendNotDispatched";
870 webapps.bindee = mockedWebView;
871
872@@ -94,7 +94,7 @@
873
874 var backend = {This: { Is: { A: {Backend: action } } } };
875
876- webapps._opt_backendProxies = backend;
877+ webapps.customBackendProxies = backend;
878 webapps.name = "test_callbacksAreWrapped";
879 webapps.bindee = mockedWebView;
880
881
882=== modified file 'tools/qml-launcher/qml-launcher.cpp'
883--- tools/qml-launcher/qml-launcher.cpp 2014-07-08 19:27:21 +0000
884+++ tools/qml-launcher/qml-launcher.cpp 2014-07-18 17:03:51 +0000
885@@ -119,17 +119,17 @@
886
887 QString value = argument.right(argument.count() - argument.indexOf(VALUE_HEADER) - 1);
888
889- qDebug() << "Adding property: "
890+ qDebug() << "Adding property:"
891 << property
892- << ", "
893- << "value: "
894+ << ","
895+ << "value:"
896 << value;
897
898 properties.insert(property, value);
899 }
900 else
901 {
902- qDebug() << "Ignoring argument: " << argument;
903+ qDebug() << "Ignoring argument:" << argument;
904 }
905 }
906
907@@ -147,13 +147,13 @@
908 QFileInfo f(qmlfile);
909 if (!f.exists() || !f.isFile())
910 {
911- qDebug() << "QML file not found or not a file: " << qmlfile;
912+ qDebug() << "QML file not found or not a file:" << qmlfile;
913 return EXIT_FAILURE;
914 }
915
916 if ( ! importPath.isEmpty())
917 {
918- qDebug() << "Setting import path to: " << importPath;
919+ qDebug() << "Setting import path to:" << importPath;
920 qputenv("QML2_IMPORT_PATH", importPath.toLatin1());
921 }
922
923@@ -167,7 +167,7 @@
924
925 if ( ! inspector.isEmpty())
926 {
927- qDebug() << "Inspector server being set to: " << inspector;
928+ qDebug() << "Inspector server being set to:" << inspector;
929
930 qputenv("QTWEBKIT_INSPECTOR_SERVER", inspector.toLatin1());
931 }
932@@ -202,7 +202,6 @@
933 if (useOxide) {
934 object->setProperty("useOxide", true);
935 }
936-
937 component.completeCreate();
938
939 if (window)

Subscribers

People subscribed via source and target branches

to all changes: