Merge lp:~abreu-alexandre/webbrowser-app/add-scheme-filter-handler into lp:webbrowser-app
- add-scheme-filter-handler
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Alexandre Abreu |
Approved revision: | 1146 |
Merged at revision: | 1184 |
Proposed branch: | lp:~abreu-alexandre/webbrowser-app/add-scheme-filter-handler |
Merge into: | lp:webbrowser-app |
Diff against target: |
1139 lines (+440/-334) 12 files modified
src/app/webcontainer/CMakeLists.txt (+2/-1) src/app/webcontainer/intent-parser.cpp (+86/-0) src/app/webcontainer/intent-parser.h (+51/-0) src/app/webcontainer/scheme-filter.cpp (+148/-169) src/app/webcontainer/scheme-filter.h (+23/-70) src/app/webcontainer/webapp-container.cpp (+24/-21) src/app/webcontainer/webapp-container.h (+4/-4) src/app/webcontainer/webapp-container.qml (+25/-26) tests/autopilot/webapp_container/tests/__init__.py (+4/-4) tests/autopilot/webapp_container/tests/test_scheme_filter.py (+59/-16) tests/unittests/intent-filter/CMakeLists.txt (+2/-1) tests/unittests/intent-filter/tst_IntentFilterTests.cpp (+12/-22) |
To merge this branch: | bzr merge lp:~abreu-alexandre/webbrowser-app/add-scheme-filter-handler |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Olivier Tilloy | Approve | ||
David Barth (community) | Needs Fixing | ||
Review via email:
|
Commit message
Add a general URI reformatting mechanism for webapp specific handled schemes.
Description of the change
Add a general URI reformatting mechanism for webapp specific handled schemes.

PS Jenkins bot (ps-jenkins) wrote : | # |

Olivier Tilloy (osomon) wrote : | # |
Please revert the changes to webbrowser-app.pot.

Alexandre Abreu (abreu-alexandre) wrote : | # |
done

David Barth (dbarth) wrote : | # |
in webapp-

David Barth (dbarth) wrote : | # |
Also, some debug log for when the filter file can't be loaded would be nice

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1107
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:1107
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://
- 1116. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1117. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1118. By Steve Langasek
-
Add missing changelog entry due to direct upload.
- 1119. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1120. By Olivier Tilloy
-
Update all imports of:
- Ubuntu.Components to 1.3
- QtQuick to 2.4
- QtQuick.Window to 2.2This bumps the required versions of qtdeclarative5-
ubuntu- ui-toolkit- plugin, qml-module-qtquick2 and qml-module- qtquick- window2 in debian/control. Fixes: #1483279 - 1121. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150810- 0ubuntu1 - 1122. By Olivier Tilloy
-
Implement the "Find in Page" feature.
This bumps the runtime dependency on liboxideqt-qmlplugin to 1.8. Fixes: #1312260
Approved by: Riccardo Padovani - 1123. By Olivier Tilloy
-
Drop old names for QML dependencies.
Original patch by Robert Ancell. - 1124. By Olivier Tilloy
-
Highlight matching terms in one pass. Fixes: #1481206
Approved by: PS Jenkins bot, Ugo Riboni - 1125. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150811- 0ubuntu1 - 1126. By Olivier Tilloy
-
Update translation template.
- 1127. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1128. By Arthur Mello
-
Wide screen versions of the history view and new tab view, per design specification.
This adds a build dependency on qml-module-qt-labs- settings (for unit tests). Fixes: #1351157, #1481647 - 1129. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150812. 1-0ubuntu1 - 1130. By Olivier Tilloy
-
Update translation template.
- 1131. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1132. By Olivier Tilloy
-
Remove strong typing of the contextualActions and selectionActions properties.
This would cause issues if an app imported Ubuntu.Web along with Ubuntu.Components < 1.3 and overrode one of these properties, because the webview expected an ActionList 1.3, and would get an earlier version. Fixes: #1484437 - 1133. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150813. 1-0ubuntu1 - 1134. By CI Train Bot Account
-
Resync trunk.
- 1135. By Ugo Riboni
-
Delay the exit from fullscreen mode until focus remains lost for a certain amount of time. Fixes: #1477308
Approved by: Olivier Tilloy - 1136. By Ugo Riboni
-
Disable find in page when the new tab view is active. Fixes: #1483847
Approved by: Olivier Tilloy - 1137. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150814- 0ubuntu1 - 1138. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1139. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.
- 1140. By Launchpad Translations on behalf of phablet-team
-
Launchpad automatic translations update.

Olivier Tilloy (osomon) wrote : | # |
Docstrings in scheme-filter.h are outdated, and to be honest not very useful as they don’t contain any actual documentation. Can they be either updated, or removed?
On a related note, some minimal documentation at the class-level to explain the general purpose of the class and how it is used in conjunction with other constructs by the webapp container would be very helpful, especially for our future selves.
intent-parser.h has some unused includes (QObject, QVariantMap). The include for QUrl could be made a forward declaration.
intent-parser.cpp has some unused includes (QJSEngine, QJSValue).
The copyright notice for the two new files should add 2015 (it’s not new code, but those are new files).
In webapp-
In test_default_
- 1141. By Colin Watson
-
Fix versioned dependencies on qtdeclarative5-
ubuntu- ui-toolkit- plugin to allow qtdeclarative5- ubuntu- ui-toolkit- plugin- gles.
Approved by: Olivier Tilloy - 1142. By Arthur Mello
-
Wait for OptionSelector's collapsing transition to finish Fixes: #1484175
Approved by: Olivier Tilloy - 1143. By Arthur Mello
-
Make sure to set the historyModel to the HistoryView component when browser wide property changes Fixes: #1484555
Approved by: Olivier Tilloy - 1144. By CI Train Bot Account
-
Releasing 0.23+15.
10.20150817- 0ubuntu1 - 1145. By Alexandre Abreu
-
Add a general URI reformatting mechanism for webapp specific handled schemes.
- 1146. By Alexandre Abreu
-
Fix test

Alexandre Abreu (abreu-alexandre) wrote : | # |
> Docstrings in scheme-filter.h are outdated, and to be honest not very useful
> as they don’t contain any actual documentation. Can they be either updated, or
> removed?
>
> On a related note, some minimal documentation at the class-level to explain
> the general purpose of the class and how it is used in conjunction with other
> constructs by the webapp container would be very helpful, especially for our
> future selves.
>
> intent-parser.h has some unused includes (QObject, QVariantMap). The include
> for QUrl could be made a forward declaration.
>
> intent-parser.cpp has some unused includes (QJSEngine, QJSValue).
>
> The copyright notice for the two new files should add 2015 (it’s not new code,
> but those are new files).
> In test_default_
> encodeURICompon
all updated,
> In webapp-
> scheme filter (LOCAL_
> already published apps?
Last time I looked no app uses a default filter for intents, the default one
does the work already,

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1146
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://

Olivier Tilloy (osomon) wrote : | # |
Once you’ve confirmed that not a single application in the store currently ships a "local-

PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1146
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://

Alexandre Abreu (abreu-alexandre) wrote : | # |
I checked again and no webapp uses a local-intent-

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1146
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 : | # |
FAILED: Continuous integration, rev:1146
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:1146
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://
Preview Diff
1 | === modified file 'src/app/webcontainer/CMakeLists.txt' |
2 | --- src/app/webcontainer/CMakeLists.txt 2015-07-03 16:13:49 +0000 |
3 | +++ src/app/webcontainer/CMakeLists.txt 2015-08-20 17:55:29 +0000 |
4 | @@ -21,7 +21,8 @@ |
5 | webapp-container-helper.cpp |
6 | session-utils.cpp |
7 | url-pattern-utils.cpp |
8 | - intent-filter.cpp |
9 | + scheme-filter.cpp |
10 | + intent-parser.cpp |
11 | ) |
12 | |
13 | add_executable(${WEBAPP_CONTAINER} ${WEBAPP_CONTAINER_SRC}) |
14 | |
15 | === added file 'src/app/webcontainer/intent-parser.cpp' |
16 | --- src/app/webcontainer/intent-parser.cpp 1970-01-01 00:00:00 +0000 |
17 | +++ src/app/webcontainer/intent-parser.cpp 2015-08-20 17:55:29 +0000 |
18 | @@ -0,0 +1,86 @@ |
19 | +/* |
20 | + * Copyright 2014-2015 Canonical Ltd. |
21 | + * |
22 | + * This file is part of webbrowser-app. |
23 | + * |
24 | + * webbrowser-app is free software; you can redistribute it and/or modify |
25 | + * it under the terms of the GNU General Public License as published by |
26 | + * the Free Software Foundation; version 3. |
27 | + * |
28 | + * webbrowser-app is distributed in the hope that it will be useful, |
29 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31 | + * GNU General Public License for more details. |
32 | + * |
33 | + * You should have received a copy of the GNU General Public License |
34 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
35 | + */ |
36 | + |
37 | +#include "intent-parser.h" |
38 | + |
39 | +#include <QtCore/QRegularExpression> |
40 | +#include <QUrl> |
41 | + |
42 | + |
43 | +namespace { |
44 | + |
45 | +const char INTENT_SCHEME_STRING[] = "intent"; |
46 | +const char INTENT_START_FRAGMENT_TAG[] = "Intent"; |
47 | +const char INTENT_URI_PACKAGE_PREFIX[] = "package="; |
48 | +const char INTENT_URI_ACTION_PREFIX[] = "action="; |
49 | +const char INTENT_URI_CATEGORY_PREFIX[] = "category="; |
50 | +const char INTENT_URI_COMPONENT_PREFIX[] = "component="; |
51 | +const char INTENT_URI_SCHEME_PREFIX[] = "scheme="; |
52 | +const char INTENT_END_FRAGMENT_TAG[] = ";end"; |
53 | + |
54 | +void trimUriSeparator(QString& uriComponent) |
55 | +{ |
56 | + uriComponent.remove(QRegExp("^/*")).remove(QRegExp("/*$")); |
57 | +} |
58 | + |
59 | +} |
60 | + |
61 | +IntentUriDescription |
62 | +parseIntentUri(const QUrl& intentUri) |
63 | +{ |
64 | + IntentUriDescription result; |
65 | + if (intentUri.scheme() != INTENT_SCHEME_STRING |
66 | + || !intentUri.fragment().startsWith(INTENT_START_FRAGMENT_TAG) |
67 | + || !intentUri.fragment().endsWith(INTENT_END_FRAGMENT_TAG)) { |
68 | + return result; |
69 | + } |
70 | + |
71 | + QString host = intentUri.host(); |
72 | + trimUriSeparator(host); |
73 | + |
74 | + QString path = intentUri.path(); |
75 | + |
76 | + if (intentUri.hasQuery()) { |
77 | + path += "?" + intentUri.query(); |
78 | + trimUriSeparator(path); |
79 | + } |
80 | + |
81 | + result.host = host; |
82 | + result.uriPath = path; |
83 | + |
84 | + QStringList infos = intentUri.fragment().split(";"); |
85 | + Q_FOREACH(const QString& info, infos) { |
86 | + if (info.startsWith(INTENT_URI_PACKAGE_PREFIX)) { |
87 | + result.package = info.split(INTENT_URI_PACKAGE_PREFIX)[1]; |
88 | + } |
89 | + else if (info.startsWith(INTENT_URI_ACTION_PREFIX)) { |
90 | + result.action = info.split(INTENT_URI_ACTION_PREFIX)[1]; |
91 | + } |
92 | + else if (info.startsWith(INTENT_URI_CATEGORY_PREFIX)) { |
93 | + result.category = info.split(INTENT_URI_CATEGORY_PREFIX)[1]; |
94 | + } |
95 | + else if (info.startsWith(INTENT_URI_COMPONENT_PREFIX)) { |
96 | + result.component = info.split(INTENT_URI_COMPONENT_PREFIX)[1]; |
97 | + } |
98 | + else if (info.startsWith(INTENT_URI_SCHEME_PREFIX)) { |
99 | + result.scheme = info.split(INTENT_URI_SCHEME_PREFIX)[1]; |
100 | + } |
101 | + } |
102 | + |
103 | + return result; |
104 | +} |
105 | |
106 | === added file 'src/app/webcontainer/intent-parser.h' |
107 | --- src/app/webcontainer/intent-parser.h 1970-01-01 00:00:00 +0000 |
108 | +++ src/app/webcontainer/intent-parser.h 2015-08-20 17:55:29 +0000 |
109 | @@ -0,0 +1,51 @@ |
110 | +/* |
111 | + * Copyright 2014-2015 Canonical Ltd. |
112 | + * |
113 | + * This file is part of webbrowser-app. |
114 | + * |
115 | + * webbrowser-app is free software; you can redistribute it and/or modify |
116 | + * it under the terms of the GNU General Public License as published by |
117 | + * the Free Software Foundation; version 3. |
118 | + * |
119 | + * webbrowser-app is distributed in the hope that it will be useful, |
120 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
121 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
122 | + * GNU General Public License for more details. |
123 | + * |
124 | + * You should have received a copy of the GNU General Public License |
125 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
126 | + */ |
127 | + |
128 | +#ifndef _INTENT_PARSER_H_ |
129 | +#define _INTENT_PARSER_H_ |
130 | + |
131 | +#include <QString> |
132 | + |
133 | + |
134 | +class QUrl; |
135 | + |
136 | +struct IntentUriDescription |
137 | +{ |
138 | + QString uriPath; |
139 | + |
140 | + // optional |
141 | + QString host; |
142 | + |
143 | + QString package; |
144 | + QString action; |
145 | + QString category; |
146 | + QString component; |
147 | + QString scheme; |
148 | +}; |
149 | + |
150 | +/** |
151 | + * @brief Parse a URI that is supposed to be an intent as defined here |
152 | + * |
153 | + * https://developer.chrome.com/multidevice/android/intents |
154 | + * |
155 | + * @param intentUri |
156 | + * @return |
157 | + */ |
158 | +IntentUriDescription parseIntentUri(const QUrl& intentUri); |
159 | + |
160 | +#endif // _INTENT_PARSER_H_ |
161 | |
162 | === renamed file 'src/app/webcontainer/intent-filter.cpp' => 'src/app/webcontainer/scheme-filter.cpp' |
163 | --- src/app/webcontainer/intent-filter.cpp 2015-01-30 16:19:51 +0000 |
164 | +++ src/app/webcontainer/scheme-filter.cpp 2015-08-20 17:55:29 +0000 |
165 | @@ -16,35 +16,24 @@ |
166 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
167 | */ |
168 | |
169 | -#include "intent-filter.h" |
170 | +#include "scheme-filter.h" |
171 | |
172 | #include <QtCore/QRegularExpression> |
173 | #include <QDebug> |
174 | #include <QFile> |
175 | #include <QJSEngine> |
176 | #include <QJSValue> |
177 | +#include <QJsonArray> |
178 | +#include <QJsonDocument> |
179 | +#include <QJsonObject> |
180 | #include <QUrl> |
181 | |
182 | - |
183 | -namespace { |
184 | - |
185 | -const char INTENT_SCHEME_STRING[] = "intent"; |
186 | -const char INTENT_START_FRAGMENT_TAG[] = "Intent"; |
187 | -const char INTENT_URI_PACKAGE_PREFIX[] = "package="; |
188 | -const char INTENT_URI_ACTION_PREFIX[] = "action="; |
189 | -const char INTENT_URI_CATEGORY_PREFIX[] = "category="; |
190 | -const char INTENT_URI_COMPONENT_PREFIX[] = "component="; |
191 | -const char INTENT_URI_SCHEME_PREFIX[] = "scheme="; |
192 | -const char INTENT_END_FRAGMENT_TAG[] = ";end"; |
193 | - |
194 | -void trimUriSeparator(QString& uriComponent) |
195 | -{ |
196 | - uriComponent.remove(QRegExp("^/*")).remove(QRegExp("/*$")); |
197 | -} |
198 | - |
199 | -} |
200 | - |
201 | -class IntentFilterPrivate |
202 | +#include <QMap> |
203 | + |
204 | +#include "intent-parser.h" |
205 | + |
206 | + |
207 | +class SchemeFilterPrivate |
208 | { |
209 | public: |
210 | |
211 | @@ -52,192 +41,182 @@ |
212 | |
213 | public: |
214 | |
215 | - IntentFilterPrivate(const QString& content); |
216 | + SchemeFilterPrivate(const QMap<QString, QString>& content); |
217 | |
218 | - QJSValue evaluate(const IntentUriDescription& intent); |
219 | - QJSValue evaluate(const QString& customContent |
220 | - , const IntentUriDescription& intent); |
221 | + QJSValue evaluate(const QString & filterFunction, const QUrl& uri); |
222 | + QJSValue evaluate(const QUrl& uri); |
223 | + QJSValue evaluate(QJSValue & function, const QUrl& uri); |
224 | + bool hasFilterFor(const QUrl& uri); |
225 | |
226 | private: |
227 | |
228 | QJSValue callFunction( |
229 | QJSValue & function |
230 | - , const IntentUriDescription& intent); |
231 | + , const QString& scheme |
232 | + , const QString& uri |
233 | + , const QString& host); |
234 | |
235 | - QString _content; |
236 | + QMap<QString, QJSValue> _filterFunctionsPerScheme; |
237 | QJSEngine _engine; |
238 | - QJSValue _function; |
239 | - |
240 | }; |
241 | |
242 | // static |
243 | -const QString IntentFilterPrivate::DEFAULT_PASS_THROUGH_FILTER = |
244 | - "(function(intent) { return intent; })"; |
245 | +const QString SchemeFilterPrivate::DEFAULT_PASS_THROUGH_FILTER = |
246 | + "(function(uri) { return uri; })"; |
247 | |
248 | -IntentFilterPrivate::IntentFilterPrivate(const QString& content) |
249 | - : _content(content) |
250 | +SchemeFilterPrivate::SchemeFilterPrivate(const QMap<QString, QString>& content) |
251 | { |
252 | - if (_content.isEmpty()) |
253 | + Q_FOREACH(QString scheme, content.keys()) |
254 | { |
255 | - _content = DEFAULT_PASS_THROUGH_FILTER; |
256 | + QJSValue v = _engine.evaluate(content[scheme]); |
257 | + if (v.isCallable()) |
258 | + { |
259 | + _filterFunctionsPerScheme[scheme] = v; |
260 | + } |
261 | } |
262 | - _function = _engine.evaluate(_content); |
263 | -} |
264 | - |
265 | -QJSValue IntentFilterPrivate::callFunction(QJSValue & function |
266 | - , const IntentUriDescription& intent) |
267 | -{ |
268 | - if (!function.isCallable()) |
269 | - { |
270 | +} |
271 | + |
272 | +bool SchemeFilterPrivate::hasFilterFor(const QUrl& uri) |
273 | +{ |
274 | + return _filterFunctionsPerScheme.contains(uri.scheme()); |
275 | +} |
276 | + |
277 | +QJSValue SchemeFilterPrivate::callFunction(QJSValue & function |
278 | + , const QString& scheme |
279 | + , const QString& path |
280 | + , const QString& host) |
281 | +{ |
282 | + if (!function.isCallable()) { |
283 | qCritical() << "Invalid intent filter function (not callable)"; |
284 | return QJSValue(); |
285 | } |
286 | |
287 | QVariantMap o; |
288 | - o.insert("scheme", intent.scheme); |
289 | - o.insert("uri", intent.uriPath); |
290 | - o.insert("host", intent.host); |
291 | + o.insert("scheme", scheme); |
292 | + o.insert("path", path); |
293 | + o.insert("host", host); |
294 | |
295 | QJSValueList jsargs; |
296 | jsargs << _engine.toScriptValue(o); |
297 | return function.call(jsargs); |
298 | } |
299 | |
300 | -QJSValue IntentFilterPrivate::evaluate(const QString& customContent |
301 | - , const IntentUriDescription& intent) |
302 | -{ |
303 | - QJSValue f = _engine.evaluate(customContent); |
304 | - return callFunction(f, intent); |
305 | -} |
306 | - |
307 | -QJSValue IntentFilterPrivate::evaluate(const IntentUriDescription& intent) |
308 | -{ |
309 | - return callFunction(_function, intent); |
310 | +QJSValue SchemeFilterPrivate::evaluate(const QString & filterFunction, |
311 | + const QUrl& uri) |
312 | +{ |
313 | + QJSValue f = _engine.evaluate(filterFunction); |
314 | + return evaluate(f, uri); |
315 | +} |
316 | + |
317 | +QJSValue SchemeFilterPrivate::evaluate(const QUrl& uri) |
318 | +{ |
319 | + return evaluate(_filterFunctionsPerScheme[uri.scheme()], uri); |
320 | +} |
321 | + |
322 | +QJSValue SchemeFilterPrivate::evaluate(QJSValue & function, const QUrl& uri) |
323 | +{ |
324 | + QString scheme; |
325 | + QString path; |
326 | + QString host; |
327 | + |
328 | + if (uri.scheme() == "intent") { |
329 | + IntentUriDescription intent = parseIntentUri(uri); |
330 | + |
331 | + scheme = intent.scheme; |
332 | + path = intent.uriPath; |
333 | + host = intent.host; |
334 | + } |
335 | + else { |
336 | + scheme = uri.scheme(); |
337 | + path = uri.path(); |
338 | + host = uri.host(); |
339 | + } |
340 | + |
341 | + return callFunction( |
342 | + function, |
343 | + scheme, |
344 | + path, |
345 | + host); |
346 | } |
347 | |
348 | // static |
349 | -bool IntentFilter::isValidLocalIntentFilterFile(const QString& filename) |
350 | +QMap<QString, QString> |
351 | +SchemeFilter::parseValidLocalSchemeFilterFile( |
352 | + bool & isValid, |
353 | + const QString& filename) |
354 | { |
355 | QFile f(filename); |
356 | - if (!f.exists() || !f.open(QIODevice::ReadOnly)) |
357 | - { |
358 | - return false; |
359 | - } |
360 | - |
361 | - // Perform basic validation |
362 | - QJSEngine engine; |
363 | - QJSValue result = engine.evaluate(QString(f.readAll()), filename); |
364 | - return !result.isNull() && result.isCallable(); |
365 | -} |
366 | - |
367 | -// static |
368 | -bool IntentFilter::isValidIntentFilterResult(const QVariantMap& result) |
369 | -{ |
370 | - return result.contains("scheme") |
371 | - && result.value("scheme").canConvert(QVariant::String) |
372 | - && result.contains("uri") |
373 | - && result.value("uri").canConvert(QVariant::String); |
374 | -} |
375 | - |
376 | -// static |
377 | -bool IntentFilter::isValidIntentDescription(const IntentUriDescription& intentDescription) |
378 | -{ |
379 | - return !intentDescription.uriPath.isEmpty() |
380 | - || !intentDescription.package.isEmpty(); |
381 | -} |
382 | - |
383 | - |
384 | -IntentFilter::IntentFilter(const QString& content, QObject *parent) : |
385 | + if (!f.exists() || !f.open(QIODevice::ReadOnly)) { |
386 | + isValid = false; |
387 | + return QMap<QString, QString>(); |
388 | + } |
389 | + |
390 | + QString content = f.readAll(); |
391 | + |
392 | + QJsonDocument document(QJsonDocument::fromJson(content.toUtf8())); |
393 | + if (document.isNull() || document.isEmpty() || !document.isObject()) { |
394 | + isValid = false; |
395 | + return QMap<QString, QString>(); |
396 | + } |
397 | + |
398 | + QMap<QString, QString> parsedContent; |
399 | + |
400 | + QJsonObject root = document.object(); |
401 | + Q_FOREACH(const QString& k, root.keys()) { |
402 | + QJsonValue v = root.value(k); |
403 | + |
404 | + if (v.isString()) { |
405 | + QJSEngine engine; |
406 | + QJSValue result = engine.evaluate(v.toString(), filename); |
407 | + |
408 | + if (result.isNull() || !result.isCallable()) { |
409 | + isValid = false; |
410 | + return QMap<QString, QString>(); |
411 | + } |
412 | + parsedContent[k] = v.toString(); |
413 | + } |
414 | + } |
415 | + |
416 | + isValid = true; |
417 | + |
418 | + return parsedContent; |
419 | +} |
420 | + |
421 | +SchemeFilter::SchemeFilter(const QMap<QString, QString>& content, QObject *parent) : |
422 | QObject(parent), |
423 | - d_ptr(new IntentFilterPrivate(content)) |
424 | -{ |
425 | -} |
426 | + d_ptr(new SchemeFilterPrivate(content)) |
427 | +{} |
428 | |
429 | -IntentFilter::~IntentFilter() |
430 | +SchemeFilter::~SchemeFilter() |
431 | { |
432 | delete d_ptr; |
433 | } |
434 | |
435 | -QVariantMap IntentFilter::applyFilter(const QString& intentUri) |
436 | -{ |
437 | - Q_D(IntentFilter); |
438 | +bool SchemeFilter::hasFilterFor(const QUrl& uri) |
439 | +{ |
440 | + Q_D(SchemeFilter); |
441 | + return d->hasFilterFor(uri); |
442 | +} |
443 | + |
444 | +QVariantMap SchemeFilter::applyFilter(const QUrl& uri) |
445 | +{ |
446 | + Q_D(SchemeFilter); |
447 | + |
448 | + if (! hasFilterFor(uri)) { |
449 | + return d->evaluate( |
450 | + SchemeFilterPrivate::DEFAULT_PASS_THROUGH_FILTER, uri) |
451 | + .toVariant().toMap(); |
452 | + } |
453 | + |
454 | + QJSValue value; |
455 | + |
456 | + // Special case to parse schemes we know about & want to provide helpr w/ |
457 | + value = d->evaluate(uri); |
458 | |
459 | QVariantMap result; |
460 | - IntentUriDescription intentDescription = |
461 | - parseIntentUri(QUrl::fromUserInput(intentUri)); |
462 | - if (!isValidIntentDescription(intentDescription)) |
463 | - { |
464 | - return result; |
465 | - } |
466 | - QJSValue value = d->evaluate(intentDescription); |
467 | - if (value.isObject() |
468 | - && value.toVariant().canConvert(QVariant::Map)) |
469 | - { |
470 | - QVariantMap r = value.toVariant().toMap(); |
471 | - if (isValidIntentFilterResult(r)) |
472 | - { |
473 | - result = r; |
474 | - } |
475 | - else |
476 | - { |
477 | - // Fallback to a noop |
478 | - result = d->evaluate( |
479 | - IntentFilterPrivate::DEFAULT_PASS_THROUGH_FILTER |
480 | - , intentDescription).toVariant().toMap(); |
481 | - } |
482 | - } |
483 | - return result; |
484 | -} |
485 | - |
486 | -bool IntentFilter::isValidIntentUri(const QString& intentUri) const |
487 | -{ |
488 | - // a bit overkill but anyway ... |
489 | - return isValidIntentDescription(parseIntentUri(QUrl::fromUserInput(intentUri))); |
490 | -} |
491 | - |
492 | -IntentUriDescription |
493 | -parseIntentUri(const QUrl& intentUri) |
494 | -{ |
495 | - IntentUriDescription result; |
496 | - if (intentUri.scheme() != INTENT_SCHEME_STRING |
497 | - || !intentUri.fragment().startsWith(INTENT_START_FRAGMENT_TAG) |
498 | - || !intentUri.fragment().endsWith(INTENT_END_FRAGMENT_TAG)) |
499 | - { |
500 | - return result; |
501 | - } |
502 | - QString host = intentUri.host(); |
503 | - trimUriSeparator(host); |
504 | - QString path = intentUri.path(); |
505 | - if (intentUri.hasQuery()) |
506 | - { |
507 | - path += "?" + intentUri.query(); |
508 | - trimUriSeparator(path); |
509 | - } |
510 | - result.host = host; |
511 | - result.uriPath = path; |
512 | - QStringList infos = intentUri.fragment().split(";"); |
513 | - Q_FOREACH(const QString& info, infos) |
514 | - { |
515 | - if (info.startsWith(INTENT_URI_PACKAGE_PREFIX)) |
516 | - { |
517 | - result.package = info.split(INTENT_URI_PACKAGE_PREFIX)[1]; |
518 | - } |
519 | - else if (info.startsWith(INTENT_URI_ACTION_PREFIX)) |
520 | - { |
521 | - result.action = info.split(INTENT_URI_ACTION_PREFIX)[1]; |
522 | - } |
523 | - else if (info.startsWith(INTENT_URI_CATEGORY_PREFIX)) |
524 | - { |
525 | - result.category = info.split(INTENT_URI_CATEGORY_PREFIX)[1]; |
526 | - } |
527 | - else if (info.startsWith(INTENT_URI_COMPONENT_PREFIX)) |
528 | - { |
529 | - result.component = info.split(INTENT_URI_COMPONENT_PREFIX)[1]; |
530 | - } |
531 | - else if (info.startsWith(INTENT_URI_SCHEME_PREFIX)) |
532 | - { |
533 | - result.scheme = info.split(INTENT_URI_SCHEME_PREFIX)[1]; |
534 | - } |
535 | - } |
536 | + if (value.isObject() && value.toVariant().canConvert(QVariant::Map)) { |
537 | + result = value.toVariant().toMap(); |
538 | + } |
539 | + |
540 | return result; |
541 | } |
542 | |
543 | === renamed file 'src/app/webcontainer/intent-filter.h' => 'src/app/webcontainer/scheme-filter.h' |
544 | --- src/app/webcontainer/intent-filter.h 2015-01-30 16:19:51 +0000 |
545 | +++ src/app/webcontainer/scheme-filter.h 2015-08-20 17:55:29 +0000 |
546 | @@ -16,90 +16,43 @@ |
547 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
548 | */ |
549 | |
550 | -#ifndef _INTENT_FILTER_H_ |
551 | -#define _INTENT_FILTER_H_ |
552 | +#ifndef _SCHEME_FILTER_H_ |
553 | +#define _SCHEME_FILTER_H_ |
554 | |
555 | #include <QObject> |
556 | +#include <QMap> |
557 | #include <QString> |
558 | +#include <QUrl> |
559 | #include <QVariantMap> |
560 | |
561 | |
562 | -class QUrl; |
563 | -class IntentFilterPrivate; |
564 | -struct IntentUriDescription; |
565 | +class SchemeFilterPrivate; |
566 | |
567 | /** |
568 | - * @brief The IntentFilter class |
569 | + * @brief The SchemeFilter class |
570 | */ |
571 | -class IntentFilter : public QObject |
572 | +class SchemeFilter : public QObject |
573 | { |
574 | Q_OBJECT |
575 | |
576 | public: |
577 | - IntentFilter(const QString& content, |
578 | - QObject *parent = 0); |
579 | - ~IntentFilter(); |
580 | - |
581 | - /** |
582 | - * @brief isValidLocalIntentFilterFile |
583 | - * @return |
584 | - */ |
585 | - static bool isValidLocalIntentFilterFile(const QString& filename); |
586 | - |
587 | - /** |
588 | - * @brief isValidIntentDescription |
589 | - * @return |
590 | - */ |
591 | - static bool isValidIntentDescription(const IntentUriDescription& ); |
592 | - |
593 | - /** |
594 | - * @brief isValidIntentFilterResult |
595 | - * @return |
596 | - */ |
597 | - static bool isValidIntentFilterResult(const QVariantMap& ); |
598 | - |
599 | - /** |
600 | - * @brief apply |
601 | - * @return |
602 | - */ |
603 | - Q_INVOKABLE QVariantMap applyFilter(const QString& intentUri); |
604 | - |
605 | - /** |
606 | - * @brief isValidIntentUri |
607 | - * @return |
608 | - */ |
609 | - Q_INVOKABLE bool isValidIntentUri(const QString& intentUri) const; |
610 | + SchemeFilter(const QMap<QString, QString>& content, |
611 | + QObject *parent = 0); |
612 | + ~SchemeFilter(); |
613 | + |
614 | + static QMap<QString, QString> parseValidLocalSchemeFilterFile( |
615 | + bool & isValid, |
616 | + const QString& filename); |
617 | + |
618 | + Q_INVOKABLE QVariantMap applyFilter(const QUrl& uri); |
619 | + |
620 | + Q_INVOKABLE bool hasFilterFor(const QUrl& uri); |
621 | |
622 | |
623 | private: |
624 | |
625 | - IntentFilterPrivate* d_ptr; |
626 | - Q_DECLARE_PRIVATE(IntentFilter) |
627 | -}; |
628 | - |
629 | - |
630 | -struct IntentUriDescription |
631 | -{ |
632 | - QString uriPath; |
633 | - |
634 | - // optional |
635 | - QString host; |
636 | - |
637 | - QString package; |
638 | - QString action; |
639 | - QString category; |
640 | - QString component; |
641 | - QString scheme; |
642 | -}; |
643 | - |
644 | -/** |
645 | - * @brief Parse a URI that is supposed to be an intent as defined here |
646 | - * |
647 | - * https://developer.chrome.com/multidevice/android/intents |
648 | - * |
649 | - * @param intentUri |
650 | - * @return |
651 | - */ |
652 | -IntentUriDescription parseIntentUri(const QUrl& intentUri); |
653 | - |
654 | -#endif // _INTENT_FILTER_H_ |
655 | + SchemeFilterPrivate* d_ptr; |
656 | + Q_DECLARE_PRIVATE(SchemeFilter) |
657 | +}; |
658 | + |
659 | +#endif // _SCHEME_FILTER_H_ |
660 | |
661 | === modified file 'src/app/webcontainer/webapp-container.cpp' |
662 | --- src/app/webcontainer/webapp-container.cpp 2015-06-24 13:37:05 +0000 |
663 | +++ src/app/webcontainer/webapp-container.cpp 2015-08-20 17:55:29 +0000 |
664 | @@ -20,7 +20,7 @@ |
665 | #include "webapp-container.h" |
666 | |
667 | #include "chrome-cookie-store.h" |
668 | -#include "intent-filter.h" |
669 | +#include "scheme-filter.h" |
670 | #include "local-cookie-store.h" |
671 | #include "online-accounts-cookie-store.h" |
672 | #include "session-utils.h" |
673 | @@ -78,7 +78,7 @@ |
674 | } |
675 | |
676 | const QString WebappContainer::URL_PATTERN_SEPARATOR = ","; |
677 | -const QString WebappContainer::LOCAL_INTENT_FILTER_FILENAME = "local-intent-filter.js"; |
678 | +const QString WebappContainer::LOCAL_SCHEME_FILTER_FILENAME = "local-scheme-filter.js"; |
679 | |
680 | |
681 | WebappContainer::WebappContainer(int& argc, char** argv): |
682 | @@ -193,8 +193,8 @@ |
683 | return false; |
684 | } |
685 | |
686 | - // Handle an optional intent filter. The default filter does nothing. |
687 | - setupLocalIntentFilterIfAny(context); |
688 | + // Handle an optional scheme handler filter. The default *catch all* filter does nothing. |
689 | + setupLocalSchemeFilterIfAny(context, m_webappModelSearchPath); |
690 | |
691 | m_component->completeCreate(); |
692 | |
693 | @@ -204,28 +204,31 @@ |
694 | } |
695 | } |
696 | |
697 | -void WebappContainer::setupLocalIntentFilterIfAny(QQmlContext* context) |
698 | +void WebappContainer::setupLocalSchemeFilterIfAny(QQmlContext* context, const QString& webappSearchPath) |
699 | { |
700 | - if(!context) |
701 | - { |
702 | + if(!context) { |
703 | return; |
704 | } |
705 | |
706 | - QString localIntentFilterFileContent; |
707 | - if (IntentFilter::isValidLocalIntentFilterFile(LOCAL_INTENT_FILTER_FILENAME)) |
708 | - { |
709 | - QFile f(LOCAL_INTENT_FILTER_FILENAME); |
710 | - if (f.open(QIODevice::ReadOnly)) |
711 | - { |
712 | - localIntentFilterFileContent = QString(f.readAll()); |
713 | - } |
714 | - f.close(); |
715 | - |
716 | - qDebug() << "Using local intent filter file:" |
717 | - << LOCAL_INTENT_FILTER_FILENAME; |
718 | + QDir searchPath(webappSearchPath.isEmpty() |
719 | + ? QDir::currentPath() |
720 | + : webappSearchPath); |
721 | + |
722 | + bool hasValidLocalSchemeFilterFile = false; |
723 | + |
724 | + QMap<QString, QString> content = |
725 | + SchemeFilter::parseValidLocalSchemeFilterFile( |
726 | + hasValidLocalSchemeFilterFile, |
727 | + searchPath.filePath(LOCAL_SCHEME_FILTER_FILENAME)); |
728 | + |
729 | + if (hasValidLocalSchemeFilterFile) { |
730 | + qDebug() << "Using local scheme filter file:" |
731 | + << LOCAL_SCHEME_FILTER_FILENAME; |
732 | } |
733 | - m_intentFilter.reset(new IntentFilter(localIntentFilterFileContent, NULL)); |
734 | - context->setContextProperty("webappIntentFilter", m_intentFilter.data()); |
735 | + |
736 | + m_schemeFilter.reset(new SchemeFilter(content)); |
737 | + |
738 | + context->setContextProperty("webappSchemeFilter", m_schemeFilter.data()); |
739 | } |
740 | |
741 | bool WebappContainer::isValidLocalApplicationRunningContext() const |
742 | |
743 | === modified file 'src/app/webcontainer/webapp-container.h' |
744 | --- src/app/webcontainer/webapp-container.h 2015-06-24 13:37:05 +0000 |
745 | +++ src/app/webcontainer/webapp-container.h 2015-08-20 17:55:29 +0000 |
746 | @@ -28,7 +28,7 @@ |
747 | #include <QStringList> |
748 | #include <QScopedPointer> |
749 | |
750 | -class IntentFilter; |
751 | +class SchemeFilter; |
752 | class QQmlContext; |
753 | |
754 | class WebappContainer : public BrowserApplication |
755 | @@ -54,7 +54,7 @@ |
756 | bool isValidLocalResource(const QString& resourceName) const; |
757 | bool shouldNotValidateCommandLineUrls() const; |
758 | bool isValidLocalIntentFilterFile(const QString& filename) const; |
759 | - void setupLocalIntentFilterIfAny(QQmlContext* context); |
760 | + void setupLocalSchemeFilterIfAny(QQmlContext* context, const QString& webappSearchPath); |
761 | |
762 | private: |
763 | QString m_webappName; |
764 | @@ -71,10 +71,10 @@ |
765 | QString m_localCookieStoreDbPath; |
766 | QString m_userAgentOverride; |
767 | QScopedPointer<WebappContainerHelper> m_webappContainerHelper; |
768 | - QScopedPointer<IntentFilter> m_intentFilter; |
769 | + QScopedPointer<SchemeFilter> m_schemeFilter; |
770 | |
771 | static const QString URL_PATTERN_SEPARATOR; |
772 | - static const QString LOCAL_INTENT_FILTER_FILENAME; |
773 | + static const QString LOCAL_SCHEME_FILTER_FILENAME; |
774 | }; |
775 | |
776 | #endif // __WEBAPP_CONTAINER_H__ |
777 | |
778 | === modified file 'src/app/webcontainer/webapp-container.qml' |
779 | --- src/app/webcontainer/webapp-container.qml 2015-08-10 15:22:00 +0000 |
780 | +++ src/app/webcontainer/webapp-container.qml 2015-08-20 17:55:29 +0000 |
781 | @@ -32,7 +32,6 @@ |
782 | |
783 | property string localCookieStoreDbPath: "" |
784 | |
785 | - property var intentFilterHandler |
786 | property string url: "" |
787 | property url webappIcon: "" |
788 | property string webappName: "" |
789 | @@ -54,7 +53,7 @@ |
790 | title: getWindowTitle() |
791 | |
792 | // Used for testing |
793 | - signal intentUriHandleResult(string uri) |
794 | + signal schemeUriHandleFilterResult(string uri) |
795 | |
796 | function getWindowTitle() { |
797 | var webappViewTitle = |
798 | @@ -229,13 +228,13 @@ |
799 | showWebView() |
800 | } |
801 | |
802 | - function makeUrlFromIntentResult(intentFilterResult) { |
803 | + function makeUrlFromResult(result) { |
804 | var scheme = null |
805 | var hostname = null |
806 | var url = root.currentWebview.url || root.url |
807 | - if (intentFilterResult.host |
808 | - && intentFilterResult.host.length !== 0) { |
809 | - hostname = intentFilterResult.host |
810 | + if (result.host |
811 | + && result.host.length !== 0) { |
812 | + hostname = result.host |
813 | } |
814 | else { |
815 | var matchHostname = url.toString().match(/.*:\/\/([^/]*)\/.*/) |
816 | @@ -243,9 +242,10 @@ |
817 | hostname = matchHostname[1] |
818 | } |
819 | } |
820 | - if (intentFilterResult.scheme |
821 | - && intentFilterResult.scheme.length !== 0) { |
822 | - scheme = intentFilterResult.scheme |
823 | + |
824 | + if (result.scheme |
825 | + && result.scheme.length !== 0) { |
826 | + scheme = result.scheme |
827 | } |
828 | else { |
829 | var matchScheme = url.toString().match(/(.*):\/\/[^/]*\/.*/) |
830 | @@ -257,33 +257,32 @@ |
831 | + '://' |
832 | + hostname |
833 | + "/" |
834 | - + (intentFilterResult.uri |
835 | - ? intentFilterResult.uri : "") |
836 | + + (result.path |
837 | + ? result.path : "") |
838 | } |
839 | |
840 | /** |
841 | - * Identity function for non-intent URIs. |
842 | * |
843 | - * Otherwise if the URI is an intent, tries to apply a webapp |
844 | - * local filter (or identity) and reconstruct the target URI based |
845 | - * on its result. |
846 | */ |
847 | - function handleIntentUri(uri) { |
848 | - var _uri = uri; |
849 | - if (webappIntentFilter |
850 | - && webappIntentFilter.isValidIntentUri(_uri)) { |
851 | - var result = webappIntentFilter.applyFilter(_uri) |
852 | - _uri = makeUrlFromIntentResult(result) |
853 | - |
854 | - console.log("Intent URI '" + uri + "' was mapped to '" + _uri + "'") |
855 | + function translateHandlerUri(uri) { |
856 | + // |
857 | + var scheme = uri.substr(0, uri.indexOf(":")) |
858 | + if (scheme.indexOf("http") === 0) { |
859 | + schemeUriHandleFilterResult(uri) |
860 | + return uri |
861 | } |
862 | |
863 | + var result = webappSchemeFilter.applyFilter(uri) |
864 | + var mapped_uri = makeUrlFromResult(result) |
865 | + |
866 | + uri = mapped_uri |
867 | + |
868 | // Report the result of the intent uri filtering (if any) |
869 | // Done for testing purposed. It is not possible at this point |
870 | // to have AP call a slot and retrieve its result synchronously. |
871 | - intentUriHandleResult(_uri) |
872 | + schemeUriHandleFilterResult(uri) |
873 | |
874 | - return _uri |
875 | + return uri |
876 | } |
877 | |
878 | // Handle runtime requests to open urls as defined |
879 | @@ -306,7 +305,7 @@ |
880 | return; |
881 | } |
882 | |
883 | - requestedUrl = handleIntentUri(requestedUrl); |
884 | + requestedUrl = translateHandlerUri(requestedUrl); |
885 | |
886 | // Add a small guard to prevent browsing to invalid urls |
887 | if (currentWebview |
888 | |
889 | === modified file 'tests/autopilot/webapp_container/tests/__init__.py' |
890 | --- tests/autopilot/webapp_container/tests/__init__.py 2015-04-29 22:18:55 +0000 |
891 | +++ tests/autopilot/webapp_container/tests/__init__.py 2015-08-20 17:55:29 +0000 |
892 | @@ -102,17 +102,17 @@ |
893 | Eventually(Equals(100), timeout=20)) |
894 | self.assertThat(webview.loading, Eventually(Equals(False))) |
895 | |
896 | - def get_intent_filtered_uri(self, uri): |
897 | + def get_scheme_filtered_uri(self, uri): |
898 | webviewContainer = self.get_webcontainer_window() |
899 | watcher = webviewContainer.watch_signal( |
900 | - 'intentUriHandleResult(QString)') |
901 | + 'schemeUriHandleFilterResult(QString)') |
902 | previous = watcher.num_emissions |
903 | - webviewContainer.slots.handleIntentUri(uri) |
904 | + webviewContainer.slots.translateHandlerUri(uri) |
905 | self.assertThat( |
906 | lambda: watcher.num_emissions, |
907 | Eventually(GreaterThan(previous))) |
908 | result = webviewContainer.get_signal_emissions( |
909 | - 'intentUriHandleResult(QString)')[-1][0] |
910 | + 'schemeUriHandleFilterResult(QString)')[-1][0] |
911 | return result |
912 | |
913 | def browse_to(self, url): |
914 | |
915 | === renamed file 'tests/autopilot/webapp_container/tests/test_intent_uri_support.py' => 'tests/autopilot/webapp_container/tests/test_scheme_filter.py' |
916 | --- tests/autopilot/webapp_container/tests/test_intent_uri_support.py 2015-01-29 16:53:45 +0000 |
917 | +++ tests/autopilot/webapp_container/tests/test_scheme_filter.py 2015-08-20 17:55:29 +0000 |
918 | @@ -25,7 +25,7 @@ |
919 | |
920 | |
921 | @contextmanager |
922 | -def generate_temp_webapp_with_intent(intent_filter_content=""): |
923 | +def generate_webapp_with_scheme_filter(scheme_filter_content=""): |
924 | tmpdir = tempfile.mkdtemp() |
925 | manifest_content = """ |
926 | { |
927 | @@ -38,10 +38,10 @@ |
928 | manifest_file = "{}/webapp-properties.json".format(tmpdir) |
929 | with open(manifest_file, "w+") as f: |
930 | f.write(manifest_content) |
931 | - if len(intent_filter_content) != 0: |
932 | - intent_filter_file = "{}/local-intent-filter.js".format(tmpdir) |
933 | - with open(intent_filter_file, "w+") as f: |
934 | - f.write(intent_filter_content) |
935 | + if len(scheme_filter_content) != 0: |
936 | + scheme_filter_file = "{}/local-scheme-filter.js".format(tmpdir) |
937 | + with open(scheme_filter_file, "w+") as f: |
938 | + f.write(scheme_filter_content) |
939 | old_cwd = os.getcwd() |
940 | try: |
941 | os.chdir(tmpdir) |
942 | @@ -51,16 +51,16 @@ |
943 | shutil.rmtree(tmpdir) |
944 | |
945 | |
946 | -# Those tests rely on get_intent_filtered_uri() which |
947 | +# Those tests rely on get_scheme_filtered_uri() which |
948 | # relies on implementation detail to trigger part of the intent handling |
949 | # code. This comes from the fact that the url-dispatcher is not easily |
950 | # instrumentable , so a full feature flow coverage is quite tricky to get. |
951 | # Those tests are not really functional in that sense. |
952 | -class WebappContainerIntentUriSupportTestCase( |
953 | +class WebappContainerSchemeFilterTestCase( |
954 | WebappContainerTestCaseWithLocalContentBase): |
955 | def test_basic_intent_parsing(self): |
956 | rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() |
957 | - with generate_temp_webapp_with_intent() as webapp_install_path: |
958 | + with generate_webapp_with_scheme_filter() as webapp_install_path: |
959 | args = ['--webappModelSearchPath='+webapp_install_path] |
960 | self.launch_webcontainer_app( |
961 | args, |
962 | @@ -73,12 +73,12 @@ |
963 | #Intent;scheme=http;package=com.google.android.apps.maps;end' |
964 | self.assertThat( |
965 | 'http://maps.google.es/maps?ie=utf-8&gl=es', |
966 | - Equals(self.get_intent_filtered_uri(intent_uri))) |
967 | + Equals(self.get_scheme_filtered_uri(intent_uri))) |
968 | |
969 | def test_webapp_with_invalid_default_local_intent(self): |
970 | rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() |
971 | - filter = "1" |
972 | - with generate_temp_webapp_with_intent(filter) as webapp_install_path: |
973 | + filter = "{ \"intent\": 1 }" |
974 | + with generate_webapp_with_scheme_filter(filter) as webapp_install_path: |
975 | args = ['--webappModelSearchPath='+webapp_install_path] |
976 | self.launch_webcontainer_app( |
977 | args, |
978 | @@ -91,16 +91,16 @@ |
979 | #Intent;scheme=http;package=com.google.android.apps.maps;end' |
980 | self.assertThat( |
981 | 'http://www.test.com/maps?ie=utf-8&gl=es', |
982 | - Equals(self.get_intent_filtered_uri(intent_uri))) |
983 | + Equals(self.get_scheme_filtered_uri(intent_uri))) |
984 | |
985 | def test_with_valid_default_local_intent(self): |
986 | rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() |
987 | - filter = "(function(r) { \ |
988 | + filter = "{ \"intent\": \"(function(r) { \ |
989 | return { \ |
990 | 'scheme': 'https', \ |
991 | 'host': 'maps.test.com', \ |
992 | - 'uri': r.uri }; })" |
993 | - with generate_temp_webapp_with_intent(filter) as webapp_install_path: |
994 | + 'path': r.path }; })\" }" |
995 | + with generate_webapp_with_scheme_filter(filter) as webapp_install_path: |
996 | args = ['--webappModelSearchPath='+webapp_install_path] |
997 | self.launch_webcontainer_app( |
998 | args, |
999 | @@ -113,4 +113,47 @@ |
1000 | #Intent;scheme=http;package=com.google.android.apps.maps;end' |
1001 | self.assertThat( |
1002 | 'https://maps.test.com/maps?ie=utf-8&gl=es', |
1003 | - Equals(self.get_intent_filtered_uri(intent_uri))) |
1004 | + Equals(self.get_scheme_filtered_uri(intent_uri))) |
1005 | + |
1006 | + def test_no_filter_for_http(self): |
1007 | + rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() |
1008 | + filter = "{ \"http\": \"(function(r) { \ |
1009 | + return { \ |
1010 | + 'scheme': 'https', \ |
1011 | + 'host': 'maps.test.com', \ |
1012 | + 'path': r.path }; })\" }" |
1013 | + with generate_webapp_with_scheme_filter(filter) as webapp_install_path: |
1014 | + args = ['--webappModelSearchPath='+webapp_install_path] |
1015 | + self.launch_webcontainer_app( |
1016 | + args, |
1017 | + {'UBUNTU_WEBVIEW_HOST_MAPPING_RULES': rule}) |
1018 | + |
1019 | + webview = self.get_oxide_webview() |
1020 | + webapp_url = 'http://www.test.com/' |
1021 | + self.assertThat(webview.url, Eventually(Equals(webapp_url))) |
1022 | + |
1023 | + new_uri = 'http://www.test.com/maps?ie=utf-8&gl=es' |
1024 | + self.assertThat( |
1025 | + 'http://www.test.com/maps?ie=utf-8&gl=es', |
1026 | + Equals(self.get_scheme_filtered_uri(new_uri))) |
1027 | + |
1028 | + def test_default_scheme_filter(self): |
1029 | + rule = 'MAP *.test.com:80 ' + self.get_base_url_hostname() |
1030 | + filter = "{ \"mailto\": \"(function(r) { \ |
1031 | + return { \ |
1032 | + 'scheme': 'https', \ |
1033 | + 'host': 'mail.google.com', \ |
1034 | + 'path': '?to='+encodeURIComponent(r.path) }; })\" }" |
1035 | + with generate_webapp_with_scheme_filter(filter) as webapp_install_path: |
1036 | + args = ['--webappModelSearchPath='+webapp_install_path] |
1037 | + self.launch_webcontainer_app( |
1038 | + args, |
1039 | + {'UBUNTU_WEBVIEW_HOST_MAPPING_RULES': rule}) |
1040 | + webview = self.get_oxide_webview() |
1041 | + webapp_url = 'http://www.test.com/' |
1042 | + self.assertThat(webview.url, Eventually(Equals(webapp_url))) |
1043 | + |
1044 | + scheme_uri = 'mailto:blabla@ubuntu.com' |
1045 | + self.assertThat( |
1046 | + 'https://mail.google.com/?to=blabla%40ubuntu.com', |
1047 | + Equals(self.get_scheme_filtered_uri(scheme_uri))) |
1048 | |
1049 | === modified file 'tests/unittests/intent-filter/CMakeLists.txt' |
1050 | --- tests/unittests/intent-filter/CMakeLists.txt 2015-06-22 10:29:20 +0000 |
1051 | +++ tests/unittests/intent-filter/CMakeLists.txt 2015-08-20 17:55:29 +0000 |
1052 | @@ -3,7 +3,8 @@ |
1053 | find_package(Qt5Test REQUIRED) |
1054 | set(TEST tst_IntentFilterTests) |
1055 | set(SOURCES |
1056 | - ${webapp-container_SOURCE_DIR}/intent-filter.cpp |
1057 | + ${webapp-container_SOURCE_DIR}/intent-parser.cpp |
1058 | + ${webapp-container_SOURCE_DIR}/scheme-filter.cpp |
1059 | tst_IntentFilterTests.cpp |
1060 | ) |
1061 | include_directories(${webapp-container_SOURCE_DIR}) |
1062 | |
1063 | === modified file 'tests/unittests/intent-filter/tst_IntentFilterTests.cpp' |
1064 | --- tests/unittests/intent-filter/tst_IntentFilterTests.cpp 2015-01-30 16:31:15 +0000 |
1065 | +++ tests/unittests/intent-filter/tst_IntentFilterTests.cpp 2015-08-20 17:55:29 +0000 |
1066 | @@ -21,7 +21,8 @@ |
1067 | #include <QtTest/QtTest> |
1068 | |
1069 | // local |
1070 | -#include "intent-filter.h" |
1071 | +#include "scheme-filter.h" |
1072 | +#include "intent-parser.h" |
1073 | |
1074 | class IntentFilterTests : public QObject |
1075 | { |
1076 | @@ -144,12 +145,6 @@ |
1077 | QCOMPARE(d.action, action); |
1078 | QCOMPARE(d.component, component); |
1079 | QCOMPARE(d.category, category); |
1080 | - |
1081 | - QVERIFY(IntentFilter::isValidIntentDescription(d) == isValid); |
1082 | - |
1083 | - QString emptyContent; |
1084 | - IntentFilter pf(emptyContent); |
1085 | - QVERIFY(pf.isValidIntentUri(intentUris) == isValid); |
1086 | } |
1087 | |
1088 | void applyFilters_data() |
1089 | @@ -171,24 +166,17 @@ |
1090 | |
1091 | QTest::newRow("Valid intent - default filter function") |
1092 | << "intent://scan/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end" |
1093 | - << "(function(result) {return {'scheme': result.scheme+'custom', 'uri': result.uri+'custom', 'host': result.host+'custom'}; })" |
1094 | + << "(function(result) {return {'scheme': result.scheme+'custom', 'path': result.path+'custom', 'host': result.host+'custom'}; })" |
1095 | << "zxingcustom" |
1096 | << "/custom" |
1097 | << "scancustom"; |
1098 | |
1099 | QTest::newRow("Valid intent - no (optional) host in filter result") |
1100 | << "intent://host/my/long/path?a=1/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end" |
1101 | - << "(function(result) {return {'scheme': result.scheme+'custom', 'uri': result.uri+'custom' }; })" |
1102 | + << "(function(result) {return {'scheme': result.scheme+'custom', 'path': result.path+'custom' }; })" |
1103 | << "zxingcustom" |
1104 | << "my/long/path?a=1custom" |
1105 | << ""; |
1106 | - |
1107 | - QTest::newRow("Valid intent - invalid filter fallback to default") |
1108 | - << "intent://host/my/long/path?a=1/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end" |
1109 | - << "(function(result) {return { 'uri': result.uri+'custom' }; })" |
1110 | - << "zxing" |
1111 | - << "my/long/path?a=1" |
1112 | - << "host"; |
1113 | } |
1114 | |
1115 | void applyFilters() |
1116 | @@ -201,16 +189,18 @@ |
1117 | QFETCH(QString, uri); |
1118 | QFETCH(QString, host); |
1119 | |
1120 | - IntentFilter pf(filterFunctionSource); |
1121 | - QVERIFY(pf.isValidIntentUri(intentUris)); |
1122 | - |
1123 | - QVariantMap r = pf.applyFilter(intentUris); |
1124 | + QMap<QString, QString> filters; |
1125 | + filters["intent"] = filterFunctionSource; |
1126 | + |
1127 | + SchemeFilter sf(filters); |
1128 | + |
1129 | + QVariantMap r = sf.applyFilter(intentUris); |
1130 | QVERIFY(r.contains("scheme")); |
1131 | - QVERIFY(r.contains("uri")); |
1132 | + QVERIFY(r.contains("path")); |
1133 | |
1134 | QCOMPARE(r.value("scheme").toString(), scheme); |
1135 | QCOMPARE(r.value("host").toString(), host); |
1136 | - QCOMPARE(r.value("uri").toString(), uri); |
1137 | + QCOMPARE(r.value("path").toString(), uri); |
1138 | } |
1139 | }; |
1140 |
FAILED: Continuous integration, rev:1097 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 1999/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 3449 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 753 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 753 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 753/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 753 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-mako/ 2873 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3446 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3446/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 21980
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/ 1999/rebuild
http://