@ufjs/webview 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +1 -1
- package/flutter/pubspec.yaml +1 -1
- package/package.json +6 -5
- package/flutter/.dart_tool/flutter_build/dart_plugin_registrant.dart +0 -132
- package/flutter/.dart_tool/package_config.json +0 -371
- package/flutter/.dart_tool/package_config_subset +0 -241
- package/flutter/.dart_tool/version +0 -1
- package/flutter/.flutter-plugins +0 -13
- package/flutter/.flutter-plugins-dependencies +0 -1
- package/flutter/build/native_assets/macos/native_assets.yaml +0 -5
- package/flutter/build/test_cache/build/cache.dill.track.dill +0 -4
- package/flutter/build/unit_test_assets/AssetManifest.bin +0 -0
- package/flutter/build/unit_test_assets/AssetManifest.json +0 -1
- package/flutter/build/unit_test_assets/FontManifest.json +0 -1
- package/flutter/build/unit_test_assets/NOTICES.Z +0 -0
- package/flutter/build/unit_test_assets/shaders/ink_sparkle.frag +0 -0
- package/flutter/pubspec.lock +0 -468
- package/flutter/test/web_view_test.dart +0 -369
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
// The app half of specs/013-web-view. The web twin is
|
|
2
|
-
// ../../test/web-view-web.test.ts, and the payload strings asserted here are
|
|
3
|
-
// the same ones it asserts — that pair IS the "two ends, one contract" check.
|
|
4
|
-
// EagerGestureRecognizer lives here, not in material.dart — without this the
|
|
5
|
-
// whole file failed to compile, which `flutter test` reports as one failed
|
|
6
|
-
// "loading …" case rather than as a missing import.
|
|
7
|
-
import 'package:flutter/gestures.dart';
|
|
8
|
-
import 'package:flutter/material.dart';
|
|
9
|
-
import 'package:flutter_test/flutter_test.dart';
|
|
10
|
-
import 'package:flutter_fjs/flutter_fjs.dart';
|
|
11
|
-
import 'package:fjs_webview/fjs_webview.dart';
|
|
12
|
-
import 'package:webview_flutter/webview_flutter.dart';
|
|
13
|
-
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
|
|
14
|
-
|
|
15
|
-
MirrorNode nodeWith(Map<String, Object?> props) {
|
|
16
|
-
final node = MirrorNode(7, 'web-view');
|
|
17
|
-
node.props = props;
|
|
18
|
-
return node;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
void main() {
|
|
22
|
-
setUp(resetFjsWebViewWarnings);
|
|
23
|
-
|
|
24
|
-
group('payloads mirror the JS side', () {
|
|
25
|
-
test('load, error and message', () {
|
|
26
|
-
expect(
|
|
27
|
-
fjsWebViewLoadPayload('https://example.com/a'),
|
|
28
|
-
'{"src":"https://example.com/a"}',
|
|
29
|
-
);
|
|
30
|
-
expect(
|
|
31
|
-
fjsWebViewErrorPayload('https://example.com/a'),
|
|
32
|
-
'{"src":"https://example.com/a","errMsg":"web-view load failed"}',
|
|
33
|
-
);
|
|
34
|
-
expect(fjsWebViewMessagePayload('hello #1'), '{"data":"hello #1"}');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('escapes what JSON has to escape', () {
|
|
38
|
-
expect(fjsWebViewMessagePayload('a "b"\nc'), '{"data":"a \\"b\\"\\nc"}');
|
|
39
|
-
expect(fjsWebViewMessagePayload('tab\there'), '{"data":"tab\\there"}');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
group('src', () {
|
|
44
|
-
test('classifies the loadable schemes and refuses the rest', () {
|
|
45
|
-
expect(fjsClassifyWebViewSrc('https://a'), FjsWebViewSrcKind.http);
|
|
46
|
-
expect(fjsClassifyWebViewSrc('http://a'), FjsWebViewSrcKind.http);
|
|
47
|
-
expect(fjsClassifyWebViewSrc('asset://a.html'), FjsWebViewSrcKind.asset);
|
|
48
|
-
expect(fjsClassifyWebViewSrc(''), FjsWebViewSrcKind.empty);
|
|
49
|
-
expect(fjsClassifyWebViewSrc(null), FjsWebViewSrcKind.empty);
|
|
50
|
-
for (final src in const [
|
|
51
|
-
'file:///etc/passwd',
|
|
52
|
-
'javascript:alert(1)',
|
|
53
|
-
'data:text/html,x',
|
|
54
|
-
'example.com',
|
|
55
|
-
]) {
|
|
56
|
-
expect(fjsClassifyWebViewSrc(src), FjsWebViewSrcKind.unsupported,
|
|
57
|
-
reason: src);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('an asset path cannot escape the module directory', () {
|
|
62
|
-
expect(fjsWebViewAssetPath('asset://demo.html'), 'demo.html');
|
|
63
|
-
expect(fjsWebViewAssetPath('asset:///demo.html'), 'demo.html');
|
|
64
|
-
expect(fjsWebViewAssetPath('asset://../secret'), isNull);
|
|
65
|
-
expect(fjsWebViewAssetPath('asset://'), isNull);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('asset:// goes to the dev server while fjs dev is connected', () {
|
|
69
|
-
final target = fjsResolveWebViewSrc(
|
|
70
|
-
'asset://demo.html',
|
|
71
|
-
devUri: Uri.parse('http://127.0.0.1:38900/'),
|
|
72
|
-
);
|
|
73
|
-
expect(target.url, 'http://127.0.0.1:38900/modules/webview/demo.html');
|
|
74
|
-
expect(target.asset, isNull);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('asset:// is a Flutter asset without one', () {
|
|
78
|
-
final target = fjsResolveWebViewSrc('asset://demo.html');
|
|
79
|
-
// loadFlutterAsset takes a key, not a URL — hence the separate shape
|
|
80
|
-
expect(target.asset, 'assets/fjs/modules/webview/demo.html');
|
|
81
|
-
expect(target.url, isNull);
|
|
82
|
-
expect(target.suffix, isEmpty);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('an asset key is separate from the document suffix', () {
|
|
86
|
-
// FWFURLParsingError happens when `demo.html?q=hello` is used as the
|
|
87
|
-
// manifest key. The suffix is restored on the local document URL
|
|
88
|
-
// instead, so the page still receives its parameters.
|
|
89
|
-
final release = fjsResolveWebViewSrc('asset://demo.html?q=hello#top');
|
|
90
|
-
expect(release.asset, 'assets/fjs/modules/webview/demo.html');
|
|
91
|
-
expect(release.suffix, '?q=hello#top');
|
|
92
|
-
|
|
93
|
-
final dev = fjsResolveWebViewSrc(
|
|
94
|
-
'asset://demo.html?q=hello#top',
|
|
95
|
-
devUri: Uri.parse('http://127.0.0.1:38900'),
|
|
96
|
-
);
|
|
97
|
-
expect(dev.url,
|
|
98
|
-
'http://127.0.0.1:38900/modules/webview/demo.html?q=hello#top');
|
|
99
|
-
expect(dev.suffix, isEmpty);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('a root path is the app\'s own page, not the module\'s', () {
|
|
103
|
-
// classifySrc in ../../index.ts has the same three kinds; missing this
|
|
104
|
-
// branch on one end shows up as `unsupported` + a warn on that end
|
|
105
|
-
// only, never as an exception (constitution I).
|
|
106
|
-
expect(fjsClassifyWebViewSrc('/html/guide.html'),
|
|
107
|
-
FjsWebViewSrcKind.local);
|
|
108
|
-
expect(fjsWebViewLocalPath('/html/guide.html'), 'html/guide.html');
|
|
109
|
-
expect(fjsWebViewLocalPath('///html/guide.html'), 'html/guide.html');
|
|
110
|
-
expect(fjsWebViewLocalPath('/../secret'), isNull);
|
|
111
|
-
expect(fjsWebViewLocalPath('/'), isNull);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('a root path goes to the dev server while fjs dev is connected', () {
|
|
115
|
-
final target = fjsResolveWebViewSrc(
|
|
116
|
-
'/html/guide.html',
|
|
117
|
-
devUri: Uri.parse('http://127.0.0.1:38900/'),
|
|
118
|
-
);
|
|
119
|
-
expect(target.url, 'http://127.0.0.1:38900/html/guide.html');
|
|
120
|
-
expect(target.asset, isNull);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('a root path is an app asset without one', () {
|
|
124
|
-
final target = fjsResolveWebViewSrc('/html/guide.html');
|
|
125
|
-
expect(target.asset, 'assets/fjs/public/html/guide.html');
|
|
126
|
-
expect(target.url, isNull);
|
|
127
|
-
expect(target.suffix, isEmpty);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('a root path splits key and suffix the same way asset:// does', () {
|
|
131
|
-
final release = fjsResolveWebViewSrc('/html/guide.html?q=1#top');
|
|
132
|
-
expect(release.asset, 'assets/fjs/public/html/guide.html');
|
|
133
|
-
expect(release.suffix, '?q=1#top');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test('strips a fragment too', () {
|
|
137
|
-
expect(fjsWebViewStripQuery('a.html#top'), 'a.html');
|
|
138
|
-
expect(fjsWebViewStripQuery('a.html'), 'a.html');
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('http is left alone either way', () {
|
|
142
|
-
expect(fjsResolveWebViewSrc('https://example.com/a').url,
|
|
143
|
-
'https://example.com/a');
|
|
144
|
-
expect(
|
|
145
|
-
fjsResolveWebViewSrc('https://example.com/a',
|
|
146
|
-
devUri: Uri.parse('http://127.0.0.1:38900'))
|
|
147
|
-
.url,
|
|
148
|
-
'https://example.com/a',
|
|
149
|
-
);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('nothing to load for empty and unsupported', () {
|
|
153
|
-
expect(fjsResolveWebViewSrc('').isNothing, isTrue);
|
|
154
|
-
expect(fjsResolveWebViewSrc('file:///x').isNothing, isTrue);
|
|
155
|
-
expect(fjsResolveWebViewSrc('asset://../x').isNothing, isTrue);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
group('load cycle', () {
|
|
160
|
-
test('reports one terminal event per load', () {
|
|
161
|
-
final cycle = FjsWebViewLoadCycle();
|
|
162
|
-
final generation = cycle.begin();
|
|
163
|
-
expect(cycle.finish(generation), isTrue);
|
|
164
|
-
// error after load, or a second onPageFinished, is not a second event
|
|
165
|
-
expect(cycle.finish(generation), isFalse);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('drops the previous page after the src changes', () {
|
|
169
|
-
final cycle = FjsWebViewLoadCycle();
|
|
170
|
-
final first = cycle.begin();
|
|
171
|
-
final second = cycle.begin();
|
|
172
|
-
expect(cycle.finish(first), isFalse);
|
|
173
|
-
expect(cycle.finish(second), isTrue);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test('accepts messages only from the current page', () {
|
|
177
|
-
final cycle = FjsWebViewLoadCycle();
|
|
178
|
-
final first = cycle.begin();
|
|
179
|
-
expect(cycle.accepts(first), isTrue);
|
|
180
|
-
cycle.begin();
|
|
181
|
-
expect(cycle.accepts(first), isFalse);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
group('release asset navigation', () {
|
|
186
|
-
test('reattaches parameters before the first page script', () {
|
|
187
|
-
final navigation = FjsWebViewAssetNavigation('?q=hello#top');
|
|
188
|
-
expect(
|
|
189
|
-
navigation.redirect('file:///bundle/demo.html'),
|
|
190
|
-
'file:///bundle/demo.html?q=hello#top',
|
|
191
|
-
);
|
|
192
|
-
expect(
|
|
193
|
-
navigation.accepts('file:///bundle/demo.html?q=hello#top'),
|
|
194
|
-
isTrue,
|
|
195
|
-
);
|
|
196
|
-
expect(navigation.redirect('file:///bundle/demo.html'), isNull);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test('does not redirect an already parameterized URL', () {
|
|
200
|
-
final navigation = FjsWebViewAssetNavigation('?q=hello');
|
|
201
|
-
expect(
|
|
202
|
-
navigation.redirect('file:///bundle/demo.html?q=hello'),
|
|
203
|
-
isNull,
|
|
204
|
-
);
|
|
205
|
-
expect(navigation.redirect('file:///bundle/demo.html'), isNotNull);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('stops protecting the base URL after the redirected page finishes',
|
|
209
|
-
() {
|
|
210
|
-
final navigation = FjsWebViewAssetNavigation('?q=hello');
|
|
211
|
-
navigation.redirect('file:///bundle/demo.html');
|
|
212
|
-
expect(
|
|
213
|
-
navigation.shouldPreventBaseNavigation('file:///bundle/demo.html'),
|
|
214
|
-
isTrue,
|
|
215
|
-
);
|
|
216
|
-
navigation.markFinished('file:///bundle/demo.html?q=hello');
|
|
217
|
-
expect(
|
|
218
|
-
navigation.shouldPreventBaseNavigation('file:///bundle/demo.html'),
|
|
219
|
-
isFalse,
|
|
220
|
-
);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
group('box', () {
|
|
225
|
-
test('needs a bounded height, because a page has no natural one', () {
|
|
226
|
-
expect(
|
|
227
|
-
fjsWebViewFitsBox(
|
|
228
|
-
const BoxConstraints.tightFor(width: 300, height: 200)),
|
|
229
|
-
isTrue,
|
|
230
|
-
);
|
|
231
|
-
expect(
|
|
232
|
-
fjsWebViewFitsBox(const BoxConstraints(maxWidth: 300)),
|
|
233
|
-
isFalse,
|
|
234
|
-
);
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
group('widget', () {
|
|
239
|
-
testWidgets('an empty src builds nothing at all', (tester) async {
|
|
240
|
-
final events = <(int, String?)>[];
|
|
241
|
-
await tester.pumpWidget(MaterialApp(
|
|
242
|
-
home: FjsWebViewWidget(
|
|
243
|
-
node: nodeWith(const {}),
|
|
244
|
-
dispatch: (id, type, {String? text}) => events.add((type, text)),
|
|
245
|
-
),
|
|
246
|
-
));
|
|
247
|
-
expect(find.byType(SizedBox), findsOneWidget);
|
|
248
|
-
expect(events, isEmpty);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
testWidgets('an unsupported scheme warns once and loads nothing',
|
|
252
|
-
(tester) async {
|
|
253
|
-
final logs = <String>[];
|
|
254
|
-
final original = debugPrint;
|
|
255
|
-
debugPrint = (message, {wrapWidth}) => logs.add(message ?? '');
|
|
256
|
-
try {
|
|
257
|
-
await tester.pumpWidget(MaterialApp(
|
|
258
|
-
home: FjsWebViewWidget(
|
|
259
|
-
node: nodeWith(const {'src': 'file:///etc/passwd'}),
|
|
260
|
-
dispatch: (id, type, {String? text}) {},
|
|
261
|
-
),
|
|
262
|
-
));
|
|
263
|
-
} finally {
|
|
264
|
-
debugPrint = original;
|
|
265
|
-
}
|
|
266
|
-
expect(logs.where((l) => l.contains('file:///etc/passwd')), isNotEmpty);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
testWidgets('claims gestures inside the platform view', (tester) async {
|
|
270
|
-
final platform = _FakeWebViewPlatform();
|
|
271
|
-
WebViewPlatform.instance = platform;
|
|
272
|
-
addTearDown(() => WebViewPlatform.instance = _FakeWebViewPlatform());
|
|
273
|
-
|
|
274
|
-
await tester.pumpWidget(MaterialApp(
|
|
275
|
-
home: FjsWebViewWidget(
|
|
276
|
-
node: nodeWith(const {'src': 'https://example.com'}),
|
|
277
|
-
dispatch: (id, type, {String? text}) {},
|
|
278
|
-
),
|
|
279
|
-
));
|
|
280
|
-
|
|
281
|
-
final params = platform.lastWidgetParams;
|
|
282
|
-
expect(params, isNotNull);
|
|
283
|
-
expect(params!.gestureRecognizers, hasLength(1));
|
|
284
|
-
expect(
|
|
285
|
-
params.gestureRecognizers.single.constructor(),
|
|
286
|
-
isA<EagerGestureRecognizer>(),
|
|
287
|
-
);
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
class _FakeWebViewPlatform extends WebViewPlatform {
|
|
293
|
-
PlatformWebViewWidgetCreationParams? lastWidgetParams;
|
|
294
|
-
|
|
295
|
-
@override
|
|
296
|
-
PlatformWebViewController createPlatformWebViewController(
|
|
297
|
-
PlatformWebViewControllerCreationParams params,
|
|
298
|
-
) => _FakeWebViewController(params);
|
|
299
|
-
|
|
300
|
-
@override
|
|
301
|
-
PlatformWebViewWidget createPlatformWebViewWidget(
|
|
302
|
-
PlatformWebViewWidgetCreationParams params,
|
|
303
|
-
) {
|
|
304
|
-
lastWidgetParams = params;
|
|
305
|
-
return _FakeWebViewWidget(params);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
@override
|
|
309
|
-
PlatformNavigationDelegate createPlatformNavigationDelegate(
|
|
310
|
-
PlatformNavigationDelegateCreationParams params,
|
|
311
|
-
) => _FakeNavigationDelegate(params);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
class _FakeWebViewController extends PlatformWebViewController {
|
|
315
|
-
_FakeWebViewController(super.params) : super.implementation();
|
|
316
|
-
|
|
317
|
-
@override
|
|
318
|
-
Future<void> setJavaScriptMode(JavaScriptMode javaScriptMode) async {}
|
|
319
|
-
|
|
320
|
-
@override
|
|
321
|
-
Future<void> setPlatformNavigationDelegate(
|
|
322
|
-
PlatformNavigationDelegate handler,
|
|
323
|
-
) async {}
|
|
324
|
-
|
|
325
|
-
@override
|
|
326
|
-
Future<void> addJavaScriptChannel(
|
|
327
|
-
JavaScriptChannelParams javaScriptChannelParams,
|
|
328
|
-
) async {}
|
|
329
|
-
|
|
330
|
-
@override
|
|
331
|
-
Future<void> loadFlutterAsset(String key) async {}
|
|
332
|
-
|
|
333
|
-
@override
|
|
334
|
-
Future<void> loadRequest(LoadRequestParams params) async {}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
class _FakeWebViewWidget extends PlatformWebViewWidget {
|
|
338
|
-
_FakeWebViewWidget(super.params) : super.implementation();
|
|
339
|
-
|
|
340
|
-
@override
|
|
341
|
-
Widget build(BuildContext context) => const SizedBox();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
class _FakeNavigationDelegate extends PlatformNavigationDelegate {
|
|
345
|
-
_FakeNavigationDelegate(super.params) : super.implementation();
|
|
346
|
-
|
|
347
|
-
// PlatformNavigationDelegate's setters throw UnimplementedError by
|
|
348
|
-
// default, and NavigationDelegate's constructor calls every one it was
|
|
349
|
-
// given a callback for. The widget under test registers all four, so a
|
|
350
|
-
// fake that overrides none cannot be built at all.
|
|
351
|
-
@override
|
|
352
|
-
Future<void> setOnNavigationRequest(
|
|
353
|
-
NavigationRequestCallback onNavigationRequest,
|
|
354
|
-
) async {}
|
|
355
|
-
|
|
356
|
-
@override
|
|
357
|
-
Future<void> setOnPageFinished(PageEventCallback onPageFinished) async {}
|
|
358
|
-
|
|
359
|
-
@override
|
|
360
|
-
Future<void> setOnPageStarted(PageEventCallback onPageStarted) async {}
|
|
361
|
-
|
|
362
|
-
@override
|
|
363
|
-
Future<void> setOnWebResourceError(
|
|
364
|
-
WebResourceErrorCallback onWebResourceError,
|
|
365
|
-
) async {}
|
|
366
|
-
|
|
367
|
-
@override
|
|
368
|
-
Future<void> setOnHttpError(HttpResponseErrorCallback onHttpError) async {}
|
|
369
|
-
}
|