Fixed Android error in some cases when calling setServiceWorkerClient java method on ServiceWorkerManager initialization

This commit is contained in:
Lorenzo Pichilli 2022-04-22 16:24:41 +02:00
parent 7aade18701
commit ce6e9066ba
9 changed files with 409 additions and 313 deletions

View File

@ -1,3 +1,7 @@
## 5.4.0+3
- Fixed Android error in some cases when calling `setServiceWorkerClient` java method on `ServiceWorkerManager` initialization
## 5.4.0+2 ## 5.4.0+2
- Fixed Android `ChromeCustomTabsActivity` not responding to the `ActionBroadcastReceiver` - Fixed Android `ChromeCustomTabsActivity` not responding to the `ActionBroadcastReceiver`

View File

@ -59,6 +59,4 @@ Add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](https://f
## Support ## Support
Use the [develop branch](https://github.com/pichillilorenzo/flutter_inappwebview/tree/develop) when making a pull request.
Did you find this plugin useful? Please consider to [make a donation](https://inappwebview.dev/donate/) to help improve it! Did you find this plugin useful? Please consider to [make a donation](https://inappwebview.dev/donate/) to help improve it!

View File

@ -38,50 +38,6 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
channel.setMethodCallHandler(this); channel.setMethodCallHandler(this);
if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) {
serviceWorkerController = ServiceWorkerControllerCompat.getInstance(); serviceWorkerController = ServiceWorkerControllerCompat.getInstance();
serviceWorkerController.setServiceWorkerClient(new ServiceWorkerClientCompat() {
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) {
final Map<String, Object> obj = new HashMap<>();
obj.put("url", request.getUrl().toString());
obj.put("method", request.getMethod());
obj.put("headers", request.getRequestHeaders());
obj.put("isForMainFrame", request.isForMainFrame());
obj.put("hasGesture", request.hasGesture());
obj.put("isRedirect", request.isRedirect());
Util.WaitFlutterResult flutterResult;
try {
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
if (flutterResult.error != null) {
Log.e(LOG_TAG, flutterResult.error);
}
else if (flutterResult.result != null) {
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
String contentType = (String) res.get("contentType");
String contentEncoding = (String) res.get("contentEncoding");
byte[] data = (byte[]) res.get("data");
Map<String, String> responseHeaders = (Map<String, String>) res.get("headers");
Integer statusCode = (Integer) res.get("statusCode");
String reasonPhrase = (String) res.get("reasonPhrase");
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return new WebResourceResponse(contentType, contentEncoding, inputStream);
} else {
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
}
}
return null;
}
});
} else { } else {
serviceWorkerController = null; serviceWorkerController = null;
} }
@ -92,6 +48,13 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null; ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null;
switch (call.method) { switch (call.method) {
case "setServiceWorkerClient":
{
Boolean isNull = (Boolean) call.argument("isNull");
setServiceWorkerClient(isNull);
}
result.success(true);
break;
case "getAllowContentAccess": case "getAllowContentAccess":
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) { if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) {
result.success(serviceWorkerWebSettings.getAllowContentAccess()); result.success(serviceWorkerWebSettings.getAllowContentAccess());
@ -153,8 +116,61 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
} }
} }
private void setServiceWorkerClient(Boolean isNull) {
if (serviceWorkerController != null) {
serviceWorkerController.setServiceWorkerClient(isNull ? null : new ServiceWorkerClientCompat() {
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) {
final Map<String, Object> obj = new HashMap<>();
obj.put("url", request.getUrl().toString());
obj.put("method", request.getMethod());
obj.put("headers", request.getRequestHeaders());
obj.put("isForMainFrame", request.isForMainFrame());
obj.put("hasGesture", request.hasGesture());
obj.put("isRedirect", request.isRedirect());
Util.WaitFlutterResult flutterResult;
try {
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
if (flutterResult.error != null) {
Log.e(LOG_TAG, flutterResult.error);
}
else if (flutterResult.result != null) {
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
String contentType = (String) res.get("contentType");
String contentEncoding = (String) res.get("contentEncoding");
byte[] data = (byte[]) res.get("data");
Map<String, String> responseHeaders = (Map<String, String>) res.get("headers");
Integer statusCode = (Integer) res.get("statusCode");
String reasonPhrase = (String) res.get("reasonPhrase");
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return new WebResourceResponse(contentType, contentEncoding, inputStream);
} else {
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
}
}
return null;
}
});
}
}
public void dispose() { public void dispose() {
channel.setMethodCallHandler(null); channel.setMethodCallHandler(null);
if (serviceWorkerController != null) {
serviceWorkerController.setServiceWorkerClient(null);
serviceWorkerController = null;
}
plugin = null; plugin = null;
} }
} }

View File

@ -77,7 +77,7 @@ void main() {
AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true); AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
} }
group('InAppWebView', () { group('InAppWebView', () {
testWidgets('initialUrlRequest', (WidgetTester tester) async { testWidgets('initialUrlRequest', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter = Completer<InAppWebViewController>();
await tester.pumpWidget( await tester.pumpWidget(
@ -1709,13 +1709,14 @@ void main() {
group('intercept ajax request', () { group('intercept ajax request', () {
testWidgets('send string data', (WidgetTester tester) async { testWidgets('send string data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer shouldInterceptAjaxPostRequestCompleter = final Completer shouldInterceptAjaxPostRequestCompleter =
Completer<void>(); Completer<void>();
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter = final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<Map<String, dynamic>> onAjaxProgressCompleter = final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -1745,9 +1746,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptAjaxRequest: true, useShouldInterceptAjaxRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -1780,9 +1781,9 @@ void main() {
await shouldInterceptAjaxPostRequestCompleter.future; await shouldInterceptAjaxPostRequestCompleter.future;
final Map<String, dynamic> onAjaxReadyStateChangeValue = final Map<String, dynamic> onAjaxReadyStateChangeValue =
await onAjaxReadyStateChangeCompleter.future; await onAjaxReadyStateChangeCompleter.future;
final Map<String, dynamic> onAjaxProgressValue = final Map<String, dynamic> onAjaxProgressValue =
await onAjaxProgressCompleter.future; await onAjaxProgressCompleter.future;
expect( expect(
mapEquals(onAjaxReadyStateChangeValue, mapEquals(onAjaxReadyStateChangeValue,
@ -1795,13 +1796,14 @@ void main() {
}); });
testWidgets('send json data', (WidgetTester tester) async { testWidgets('send json data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer shouldInterceptAjaxPostRequestCompleter = final Completer shouldInterceptAjaxPostRequestCompleter =
Completer<void>(); Completer<void>();
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter = final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<Map<String, dynamic>> onAjaxProgressCompleter = final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -1835,15 +1837,16 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptAjaxRequest: true, useShouldInterceptAjaxRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
shouldInterceptAjaxRequest: (controller, ajaxRequest) async { shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
String data = ajaxRequest.data; String data = ajaxRequest.data;
assert(data.contains('"firstname":"Foo"') && data.contains('"lastname":"Bar"')); assert(data.contains('"firstname":"Foo"') &&
data.contains('"lastname":"Bar"'));
ajaxRequest.responseType = 'json'; ajaxRequest.responseType = 'json';
ajaxRequest.data = '{"firstname": "Foo2", "lastname": "Bar2"}'; ajaxRequest.data = '{"firstname": "Foo2", "lastname": "Bar2"}';
@ -1871,9 +1874,9 @@ void main() {
await shouldInterceptAjaxPostRequestCompleter.future; await shouldInterceptAjaxPostRequestCompleter.future;
final Map<String, dynamic> onAjaxReadyStateChangeValue = final Map<String, dynamic> onAjaxReadyStateChangeValue =
await onAjaxReadyStateChangeCompleter.future; await onAjaxReadyStateChangeCompleter.future;
final Map<String, dynamic> onAjaxProgressValue = final Map<String, dynamic> onAjaxProgressValue =
await onAjaxProgressCompleter.future; await onAjaxProgressCompleter.future;
expect( expect(
mapEquals(onAjaxReadyStateChangeValue, mapEquals(onAjaxReadyStateChangeValue,
@ -1886,13 +1889,14 @@ void main() {
}); });
testWidgets('send URLSearchParams data', (WidgetTester tester) async { testWidgets('send URLSearchParams data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer shouldInterceptAjaxPostRequestCompleter = final Completer shouldInterceptAjaxPostRequestCompleter =
Completer<void>(); Completer<void>();
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter = final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<Map<String, dynamic>> onAjaxProgressCompleter = final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -1924,9 +1928,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptAjaxRequest: true, useShouldInterceptAjaxRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -1959,9 +1963,9 @@ void main() {
await shouldInterceptAjaxPostRequestCompleter.future; await shouldInterceptAjaxPostRequestCompleter.future;
final Map<String, dynamic> onAjaxReadyStateChangeValue = final Map<String, dynamic> onAjaxReadyStateChangeValue =
await onAjaxReadyStateChangeCompleter.future; await onAjaxReadyStateChangeCompleter.future;
final Map<String, dynamic> onAjaxProgressValue = final Map<String, dynamic> onAjaxProgressValue =
await onAjaxProgressCompleter.future; await onAjaxProgressCompleter.future;
expect( expect(
mapEquals(onAjaxReadyStateChangeValue, mapEquals(onAjaxReadyStateChangeValue,
@ -1974,13 +1978,14 @@ void main() {
}); });
testWidgets('send FormData', (WidgetTester tester) async { testWidgets('send FormData', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer shouldInterceptAjaxPostRequestCompleter = final Completer shouldInterceptAjaxPostRequestCompleter =
Completer<void>(); Completer<void>();
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter = final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<Map<String, dynamic>> onAjaxProgressCompleter = final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -2012,9 +2017,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptAjaxRequest: true, useShouldInterceptAjaxRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -2025,7 +2030,9 @@ void main() {
var bodyString = String.fromCharCodes(body); var bodyString = String.fromCharCodes(body);
assert(bodyString.indexOf("WebKitFormBoundary") >= 0); assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
ajaxRequest.data = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2")); ajaxRequest.data = utf8.encode(bodyString
.replaceFirst("Foo", "Foo2")
.replaceFirst("Bar", "Bar2"));
ajaxRequest.responseType = 'json'; ajaxRequest.responseType = 'json';
shouldInterceptAjaxPostRequestCompleter.complete(controller); shouldInterceptAjaxPostRequestCompleter.complete(controller);
return ajaxRequest; return ajaxRequest;
@ -2051,9 +2058,9 @@ void main() {
await shouldInterceptAjaxPostRequestCompleter.future; await shouldInterceptAjaxPostRequestCompleter.future;
final Map<String, dynamic> onAjaxReadyStateChangeValue = final Map<String, dynamic> onAjaxReadyStateChangeValue =
await onAjaxReadyStateChangeCompleter.future; await onAjaxReadyStateChangeCompleter.future;
final Map<String, dynamic> onAjaxProgressValue = final Map<String, dynamic> onAjaxProgressValue =
await onAjaxProgressCompleter.future; await onAjaxProgressCompleter.future;
expect( expect(
mapEquals(onAjaxReadyStateChangeValue, mapEquals(onAjaxReadyStateChangeValue,
@ -2068,11 +2075,12 @@ void main() {
group('intercept fetch request', () { group('intercept fetch request', () {
testWidgets('send string data', (WidgetTester tester) async { testWidgets('send string data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer<Map<String, dynamic>> fetchPostCompleter = final Completer<Map<String, dynamic>> fetchPostCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<void> shouldInterceptFetchPostRequestCompleter = final Completer<void> shouldInterceptFetchPostRequestCompleter =
Completer<void>(); Completer<void>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -2113,9 +2121,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptFetchRequest: true, useShouldInterceptFetchRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
@ -2147,11 +2155,12 @@ void main() {
}); });
testWidgets('send json data', (WidgetTester tester) async { testWidgets('send json data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer<Map<String, dynamic>> fetchPostCompleter = final Completer<Map<String, dynamic>> fetchPostCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<void> shouldInterceptFetchPostRequestCompleter = final Completer<void> shouldInterceptFetchPostRequestCompleter =
Completer<void>(); Completer<void>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -2196,9 +2205,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptFetchRequest: true, useShouldInterceptFetchRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
@ -2211,7 +2220,8 @@ void main() {
}, },
shouldInterceptFetchRequest: (controller, fetchRequest) async { shouldInterceptFetchRequest: (controller, fetchRequest) async {
String body = fetchRequest.body; String body = fetchRequest.body;
assert(body.contains('"firstname":"Foo"') && body.contains('"lastname":"Bar"')); assert(body.contains('"firstname":"Foo"') &&
body.contains('"lastname":"Bar"'));
fetchRequest.body = '{"firstname": "Foo2", "lastname": "Bar2"}'; fetchRequest.body = '{"firstname": "Foo2", "lastname": "Bar2"}';
shouldInterceptFetchPostRequestCompleter.complete(); shouldInterceptFetchPostRequestCompleter.complete();
@ -2231,11 +2241,12 @@ void main() {
}); });
testWidgets('send URLSearchParams data', (WidgetTester tester) async { testWidgets('send URLSearchParams data', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer<Map<String, dynamic>> fetchPostCompleter = final Completer<Map<String, dynamic>> fetchPostCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<void> shouldInterceptFetchPostRequestCompleter = final Completer<void> shouldInterceptFetchPostRequestCompleter =
Completer<void>(); Completer<void>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -2278,9 +2289,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptFetchRequest: true, useShouldInterceptFetchRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
@ -2312,11 +2323,12 @@ void main() {
}); });
testWidgets('send FormData', (WidgetTester tester) async { testWidgets('send FormData', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer<Map<String, dynamic>> fetchPostCompleter = final Completer<Map<String, dynamic>> fetchPostCompleter =
Completer<Map<String, dynamic>>(); Completer<Map<String, dynamic>>();
final Completer<void> shouldInterceptFetchPostRequestCompleter = final Completer<void> shouldInterceptFetchPostRequestCompleter =
Completer<void>(); Completer<void>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -2357,9 +2369,9 @@ void main() {
"""), """),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
useShouldInterceptFetchRequest: true, useShouldInterceptFetchRequest: true,
)), )),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
@ -2377,7 +2389,9 @@ void main() {
var bodyString = String.fromCharCodes(body); var bodyString = String.fromCharCodes(body);
assert(bodyString.indexOf("WebKitFormBoundary") >= 0); assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
fetchRequest.body = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2")); fetchRequest.body = utf8.encode(bodyString
.replaceFirst("Foo", "Foo2")
.replaceFirst("Bar", "Bar2"));
shouldInterceptFetchPostRequestCompleter.complete(); shouldInterceptFetchPostRequestCompleter.complete();
return fetchRequest; return fetchRequest;
}, },
@ -2404,24 +2418,24 @@ void main() {
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialUrlRequest: initialUrlRequest:
URLRequest(url: Uri.parse('https://flutter.dev/')), URLRequest(url: Uri.parse('https://flutter.dev/')),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: crossPlatform:
InAppWebViewOptions(clearCache: true, contentBlockers: [ InAppWebViewOptions(clearCache: true, contentBlockers: [
ContentBlocker( ContentBlocker(
trigger: trigger:
ContentBlockerTrigger(urlFilter: ".*", resourceType: [ ContentBlockerTrigger(urlFilter: ".*", resourceType: [
ContentBlockerTriggerResourceType.IMAGE, ContentBlockerTriggerResourceType.IMAGE,
ContentBlockerTriggerResourceType.STYLE_SHEET ContentBlockerTriggerResourceType.STYLE_SHEET
], ifTopUrl: [ ], ifTopUrl: [
"https://flutter.dev/" "https://flutter.dev/"
]), ]),
action: ContentBlockerAction( action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK)) type: ContentBlockerActionType.BLOCK))
])), ])),
onLoadStop: (controller, url) { onLoadStop: (controller, url) {
pageLoaded.complete(); pageLoaded.complete();
}, },
@ -3261,7 +3275,7 @@ setTimeout(function() {
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialUrlRequest: initialUrlRequest:
URLRequest(url: Uri.parse('https://github.com/flutter')), URLRequest(url: Uri.parse('https://github.com/flutter')),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -3278,7 +3292,7 @@ setTimeout(function() {
); );
final InAppWebViewController controller = final InAppWebViewController controller =
await controllerCompleter.future; await controllerCompleter.future;
await pageLoaded.future; await pageLoaded.future;
listenForScaleChange = true; listenForScaleChange = true;
@ -4167,10 +4181,14 @@ setTimeout(function() {
await pageLoaded.future; await pageLoaded.future;
await controller.injectJavascriptFileFromUrl( await controller.injectJavascriptFileFromUrl(
urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'), urlFile:
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery-error', onError: () { Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'),
jQueryLoadError.complete(); scriptHtmlTagAttributes: ScriptHtmlTagAttributes(
},)); id: 'jquery-error',
onError: () {
jQueryLoadError.complete();
},
));
await jQueryLoadError.future; await jQueryLoadError.future;
expect( expect(
await controller.evaluateJavascript( await controller.evaluateJavascript(
@ -4182,9 +4200,12 @@ setTimeout(function() {
await controller.injectJavascriptFileFromUrl( await controller.injectJavascriptFileFromUrl(
urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'), urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'),
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery', onLoad: () { scriptHtmlTagAttributes: ScriptHtmlTagAttributes(
jQueryLoaded.complete(); id: 'jquery',
},)); onLoad: () {
jQueryLoaded.complete();
},
));
await jQueryLoaded.future; await jQueryLoaded.future;
expect( expect(
await controller.evaluateJavascript( await controller.evaluateJavascript(
@ -4903,21 +4924,19 @@ setTimeout(function() {
expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull); expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull);
}); });
testWidgets('launches with pull-to-refresh feature', (WidgetTester tester) async { testWidgets('launches with pull-to-refresh feature',
(WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter = Completer<InAppWebViewController>();
final pullToRefreshController = PullToRefreshController( final pullToRefreshController = PullToRefreshController(
options: PullToRefreshOptions( options: PullToRefreshOptions(
color: Colors.blue, color: Colors.blue,
size: AndroidPullToRefreshSize.DEFAULT, size: AndroidPullToRefreshSize.DEFAULT,
backgroundColor: Colors.grey, backgroundColor: Colors.grey,
enabled: true, enabled: true,
slingshotDistance: 150, slingshotDistance: 150,
distanceToTriggerSync: 150, distanceToTriggerSync: 150,
attributedTitle: IOSNSAttributedString(string: "test") attributedTitle: IOSNSAttributedString(string: "test")),
), onRefresh: () {},
onRefresh: () {
},
); );
await tester.pumpWidget( await tester.pumpWidget(
@ -4925,12 +4944,11 @@ setTimeout(function() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), initialUrlRequest:
URLRequest(url: Uri.parse('https://github.com/flutter')),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions( android:
useHybridComposition: true AndroidInAppWebViewOptions(useHybridComposition: true)),
)
),
pullToRefreshController: pullToRefreshController, pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
@ -4939,22 +4957,22 @@ setTimeout(function() {
), ),
); );
final InAppWebViewController controller = final InAppWebViewController controller =
await controllerCompleter.future; await controllerCompleter.future;
final String? currentUrl = (await controller.getUrl())?.toString(); final String? currentUrl = (await controller.getUrl())?.toString();
expect(currentUrl, 'https://github.com/flutter'); expect(currentUrl, 'https://github.com/flutter');
}); });
group('WebMessage', () { group('WebMessage', () {
testWidgets('WebMessageChannel', (WidgetTester tester) async { testWidgets('WebMessageChannel', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer webMessageCompleter = Completer<String>(); final Completer webMessageCompleter = Completer<String>();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialData: InAppWebViewInitialData( initialData: InAppWebViewInitialData(data: """
data: """
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -4989,15 +5007,20 @@ setTimeout(function() {
webMessageCompleter.complete(consoleMessage.message); webMessageCompleter.complete(consoleMessage.message);
}, },
onLoadStop: (controller, url) async { onLoadStop: (controller, url) async {
var webMessageChannel = await controller.createWebMessageChannel(); var webMessageChannel =
await controller.createWebMessageChannel();
var port1 = webMessageChannel!.port1; var port1 = webMessageChannel!.port1;
var port2 = webMessageChannel.port2; var port2 = webMessageChannel.port2;
await port1.setWebMessageCallback((message) async { await port1.setWebMessageCallback((message) async {
await port1.postMessage(WebMessage(data: message! + " and back")); await port1
.postMessage(WebMessage(data: message! + " and back"));
}); });
await controller.postWebMessage(message: WebMessage(data: "capturePort", ports: [port2]), targetOrigin: Uri.parse("*")); await controller.postWebMessage(
await controller.evaluateJavascript(source: "document.getElementById('button').click();"); message: WebMessage(data: "capturePort", ports: [port2]),
targetOrigin: Uri.parse("*"));
await controller.evaluateJavascript(
source: "document.getElementById('button').click();");
}, },
), ),
), ),
@ -5009,7 +5032,8 @@ setTimeout(function() {
}); });
testWidgets('WebMessageListener', (WidgetTester tester) async { testWidgets('WebMessageListener', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter =
Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>(); final Completer<void> pageLoaded = Completer<void>();
final Completer webMessageCompleter = Completer<String>(); final Completer webMessageCompleter = Completer<String>();
await tester.pumpWidget( await tester.pumpWidget(
@ -5021,8 +5045,10 @@ setTimeout(function() {
await controller.addWebMessageListener(WebMessageListener( await controller.addWebMessageListener(WebMessageListener(
jsObjectName: "myTestObj", jsObjectName: "myTestObj",
allowedOriginRules: Set.from(["https://*.example.com"]), allowedOriginRules: Set.from(["https://*.example.com"]),
onPostMessage: (message, sourceOrigin, isMainFrame, replyProxy) { onPostMessage:
assert(sourceOrigin.toString() == "https://www.example.com"); (message, sourceOrigin, isMainFrame, replyProxy) {
assert(
sourceOrigin.toString() == "https://www.example.com");
assert(isMainFrame); assert(isMainFrame);
replyProxy.postMessage(message! + " and back"); replyProxy.postMessage(message! + " and back");
@ -5042,7 +5068,8 @@ setTimeout(function() {
), ),
); );
final controller = await controllerCompleter.future; final controller = await controllerCompleter.future;
await controller.loadUrl(urlRequest: URLRequest(url: Uri.parse("https://www.example.com/"))); await controller.loadUrl(
urlRequest: URLRequest(url: Uri.parse("https://www.example.com/")));
await pageLoaded.future; await pageLoaded.future;
await controller.evaluateJavascript(source: """ await controller.evaluateJavascript(source: """
@ -5273,7 +5300,9 @@ setTimeout(function() {
}, skip: !Platform.isAndroid); }, skip: !Platform.isAndroid);
test('setWebContentsDebuggingEnabled', () async { test('setWebContentsDebuggingEnabled', () async {
expect(AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true), completes); expect(
AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true),
completes);
}, skip: !Platform.isAndroid); }, skip: !Platform.isAndroid);
}, skip: !Platform.isAndroid); }, skip: !Platform.isAndroid);
@ -5377,8 +5406,7 @@ setTimeout(function() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialData: InAppWebViewInitialData( initialData: InAppWebViewInitialData(data: """
data: """
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -5393,8 +5421,7 @@ setTimeout(function() {
</script> </script>
</body> </body>
</html> </html>
""" """),
),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
ios: IOSInAppWebViewOptions( ios: IOSInAppWebViewOptions(
applePayAPIEnabled: true, applePayAPIEnabled: true,
@ -5421,6 +5448,47 @@ setTimeout(function() {
}, skip: !Platform.isIOS); }, skip: !Platform.isIOS);
}); });
group('Service Worker', () {
testWidgets('AndroidInAppWebViewController', (WidgetTester tester) async {
final Completer completer = Completer();
var swAvailable = await AndroidWebViewFeature.isFeatureSupported(
AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE);
var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported(
AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST);
if (swAvailable && swInterceptAvailable) {
AndroidServiceWorkerController serviceWorkerController =
AndroidServiceWorkerController.instance();
await serviceWorkerController
.setServiceWorkerClient(AndroidServiceWorkerClient(
shouldInterceptRequest: (request) async {
if (!completer.isCompleted) {
completer.complete();
}
return null;
},
));
} else {
completer.complete();
}
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest:
URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')),
),
),
);
expect(completer.future, completes);
}, skip: !Platform.isAndroid);
});
group('Cookie Manager', () { group('Cookie Manager', () {
testWidgets('set, get, delete', (WidgetTester tester) async { testWidgets('set, get, delete', (WidgetTester tester) async {
CookieManager cookieManager = CookieManager.instance(); CookieManager cookieManager = CookieManager.instance();
@ -5474,7 +5542,8 @@ setTimeout(function() {
final Completer<void> pageLoaded = Completer<void>(); final Completer<void> pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView( var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialUrlRequest:
URLRequest(url: Uri.parse("https://github.com/flutter")),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -5503,20 +5572,20 @@ setTimeout(function() {
final Completer<void> pageLoaded = Completer<void>(); final Completer<void> pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView( var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialUrlRequest:
onWebViewCreated: (controller) { URLRequest(url: Uri.parse("https://github.com/flutter")),
controllerCompleter.complete(controller); onWebViewCreated: (controller) {
}, controllerCompleter.complete(controller);
onLoadStop: (controller, url) async { },
pageLoaded.complete(); onLoadStop: (controller, url) async {
} pageLoaded.complete();
); });
await headlessWebView.run(); await headlessWebView.run();
expect(headlessWebView.isRunning(), true); expect(headlessWebView.isRunning(), true);
final InAppWebViewController controller = final InAppWebViewController controller =
await controllerCompleter.future; await controllerCompleter.future;
await pageLoaded.future; await pageLoaded.future;
final String? url = (await controller.getUrl())?.toString(); final String? url = (await controller.getUrl())?.toString();
@ -5537,11 +5606,12 @@ setTimeout(function() {
final Completer controllerCompleter = Completer<InAppWebViewController>(); final Completer controllerCompleter = Completer<InAppWebViewController>();
var headlessWebView = new HeadlessInAppWebView( var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialUrlRequest:
initialSize: Size(600, 800), URLRequest(url: Uri.parse("https://github.com/flutter")),
onWebViewCreated: (controller) { initialSize: Size(600, 800),
controllerCompleter.complete(controller); onWebViewCreated: (controller) {
}, controllerCompleter.complete(controller);
},
); );
await headlessWebView.run(); await headlessWebView.run();
@ -5566,7 +5636,8 @@ setTimeout(function() {
final Completer<void> pageLoaded = Completer<void>(); final Completer<void> pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView( var headlessWebView = new HeadlessInAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), initialUrlRequest:
URLRequest(url: Uri.parse("https://github.com/flutter")),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)), crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
@ -5610,8 +5681,7 @@ setTimeout(function() {
expect(inAppBrowser.isOpened(), true); expect(inAppBrowser.isOpened(), true);
expect(() async { expect(() async {
await inAppBrowser.openUrlRequest( await inAppBrowser.openUrlRequest(
urlRequest: urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")));
URLRequest(url: Uri.parse("https://flutter.dev")));
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
await inAppBrowser.firstPageLoaded.future; await inAppBrowser.firstPageLoaded.future;
@ -5633,13 +5703,14 @@ setTimeout(function() {
await inAppBrowser.show(); await inAppBrowser.show();
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>())); }, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
await inAppBrowser.openFile(assetFilePath: "test_assets/in_app_webview_initial_file_test.html"); await inAppBrowser.openFile(
assetFilePath: "test_assets/in_app_webview_initial_file_test.html");
await inAppBrowser.browserCreated.future; await inAppBrowser.browserCreated.future;
expect(inAppBrowser.isOpened(), true); expect(inAppBrowser.isOpened(), true);
expect(() async { expect(() async {
await inAppBrowser.openUrlRequest( await inAppBrowser.openUrlRequest(
urlRequest: urlRequest:
URLRequest(url: Uri.parse("https://github.com/flutter"))); URLRequest(url: Uri.parse("https://github.com/flutter")));
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
await inAppBrowser.firstPageLoaded.future; await inAppBrowser.firstPageLoaded.future;
@ -5661,7 +5732,8 @@ setTimeout(function() {
await inAppBrowser.show(); await inAppBrowser.show();
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>())); }, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
await inAppBrowser.openData(data: """ await inAppBrowser.openData(
data: """
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
@ -5685,7 +5757,7 @@ setTimeout(function() {
expect(() async { expect(() async {
await inAppBrowser.openUrlRequest( await inAppBrowser.openUrlRequest(
urlRequest: urlRequest:
URLRequest(url: Uri.parse("https://github.com/flutter"))); URLRequest(url: Uri.parse("https://github.com/flutter")));
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
await inAppBrowser.firstPageLoaded.future; await inAppBrowser.firstPageLoaded.future;
@ -5728,12 +5800,12 @@ setTimeout(function() {
var chromeSafariBrowser = new MyChromeSafariBrowser(); var chromeSafariBrowser = new MyChromeSafariBrowser();
expect(chromeSafariBrowser.isOpened(), false); expect(chromeSafariBrowser.isOpened(), false);
await chromeSafariBrowser.open(url: Uri.parse("https://github.com/flutter")); await chromeSafariBrowser.open(
url: Uri.parse("https://github.com/flutter"));
await chromeSafariBrowser.browserCreated.future; await chromeSafariBrowser.browserCreated.future;
expect(chromeSafariBrowser.isOpened(), true); expect(chromeSafariBrowser.isOpened(), true);
expect(() async { expect(() async {
await chromeSafariBrowser.open( await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
url: Uri.parse("https://flutter.dev"));
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
@ -5748,21 +5820,18 @@ setTimeout(function() {
expect(chromeSafariBrowser.isOpened(), false); expect(chromeSafariBrowser.isOpened(), false);
await chromeSafariBrowser.open( await chromeSafariBrowser.open(
url: Uri.parse("https://github.com/flutter"), url: Uri.parse("https://github.com/flutter"),
options: ChromeSafariBrowserClassOptions( options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions( android:
isSingleInstance: true AndroidChromeCustomTabsOptions(isSingleInstance: true)));
)
)
);
await chromeSafariBrowser.browserCreated.future; await chromeSafariBrowser.browserCreated.future;
expect(chromeSafariBrowser.isOpened(), true); expect(chromeSafariBrowser.isOpened(), true);
expect(() async { expect(() async {
await chromeSafariBrowser.open( await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
url: Uri.parse("https://flutter.dev"));
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await expectLater(
chromeSafariBrowser.firstPageLoaded.future, completes);
await chromeSafariBrowser.close(); await chromeSafariBrowser.close();
await chromeSafariBrowser.browserClosed.future; await chromeSafariBrowser.browserClosed.future;
expect(chromeSafariBrowser.isOpened(), false); expect(chromeSafariBrowser.isOpened(), false);
@ -5776,18 +5845,15 @@ setTimeout(function() {
url: Uri.parse("https://github.com/flutter"), url: Uri.parse("https://github.com/flutter"),
options: ChromeSafariBrowserClassOptions( options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions( android: AndroidChromeCustomTabsOptions(
isTrustedWebActivity: true isTrustedWebActivity: true)));
)
)
);
await chromeSafariBrowser.browserCreated.future; await chromeSafariBrowser.browserCreated.future;
expect(chromeSafariBrowser.isOpened(), true); expect(chromeSafariBrowser.isOpened(), true);
expect(() async { expect(() async {
await chromeSafariBrowser.open( await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
url: Uri.parse("https://flutter.dev"));
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await expectLater(
chromeSafariBrowser.firstPageLoaded.future, completes);
await chromeSafariBrowser.close(); await chromeSafariBrowser.close();
await chromeSafariBrowser.browserClosed.future; await chromeSafariBrowser.browserClosed.future;
expect(chromeSafariBrowser.isOpened(), false); expect(chromeSafariBrowser.isOpened(), false);
@ -5801,19 +5867,15 @@ setTimeout(function() {
url: Uri.parse("https://github.com/flutter"), url: Uri.parse("https://github.com/flutter"),
options: ChromeSafariBrowserClassOptions( options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions( android: AndroidChromeCustomTabsOptions(
isTrustedWebActivity: true, isTrustedWebActivity: true, isSingleInstance: true)));
isSingleInstance: true
)
)
);
await chromeSafariBrowser.browserCreated.future; await chromeSafariBrowser.browserCreated.future;
expect(chromeSafariBrowser.isOpened(), true); expect(chromeSafariBrowser.isOpened(), true);
expect(() async { expect(() async {
await chromeSafariBrowser.open( await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
url: Uri.parse("https://flutter.dev"));
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>())); }, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); await expectLater(
chromeSafariBrowser.firstPageLoaded.future, completes);
await chromeSafariBrowser.close(); await chromeSafariBrowser.close();
await chromeSafariBrowser.browserClosed.future; await chromeSafariBrowser.browserClosed.future;
expect(chromeSafariBrowser.isOpened(), false); expect(chromeSafariBrowser.isOpened(), false);
@ -5837,8 +5899,8 @@ setTimeout(function() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: InAppWebView( child: InAppWebView(
key: GlobalKey(), key: GlobalKey(),
initialUrlRequest: initialUrlRequest: URLRequest(
URLRequest(url: Uri.parse('http://localhost:8080/test_assets/index.html')), url: Uri.parse('http://localhost:8080/test_assets/index.html')),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
controllerCompleter.complete(controller); controllerCompleter.complete(controller);
}, },
@ -5846,7 +5908,7 @@ setTimeout(function() {
), ),
); );
final InAppWebViewController controller = final InAppWebViewController controller =
await controllerCompleter.future; await controllerCompleter.future;
final String? currentUrl = (await controller.getUrl())?.toString(); final String? currentUrl = (await controller.getUrl())?.toString();
expect(currentUrl, 'http://localhost:8080/test_assets/index.html'); expect(currentUrl, 'http://localhost:8080/test_assets/index.html');
}); });

View File

@ -83,7 +83,6 @@ class InAppBrowserExampleScreen extends StatefulWidget {
} }
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> { class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
late PullToRefreshController pullToRefreshController; late PullToRefreshController pullToRefreshController;
@override @override
@ -99,7 +98,8 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
widget.browser.webViewController.reload(); widget.browser.webViewController.reload();
} else if (Platform.isIOS) { } else if (Platform.isIOS) {
widget.browser.webViewController.loadUrl( widget.browser.webViewController.loadUrl(
urlRequest: URLRequest(url: await widget.browser.webViewController.getUrl())); urlRequest: URLRequest(
url: await widget.browser.webViewController.getUrl()));
} }
}, },
); );

View File

@ -17,15 +17,13 @@ class InAppWebViewExampleScreen extends StatefulWidget {
} }
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> { class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
final GlobalKey webViewKey = GlobalKey(); final GlobalKey webViewKey = GlobalKey();
InAppWebViewController? webViewController; InAppWebViewController? webViewController;
InAppWebViewGroupOptions options = InAppWebViewGroupOptions( InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true, useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false mediaPlaybackRequiresUserGesture: false),
),
android: AndroidInAppWebViewOptions( android: AndroidInAppWebViewOptions(
useHybridComposition: true, useHybridComposition: true,
), ),
@ -102,9 +100,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
body: SafeArea( body: SafeArea(
child: Column(children: <Widget>[ child: Column(children: <Widget>[
TextField( TextField(
decoration: InputDecoration( decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
prefixIcon: Icon(Icons.search)
),
controller: urlController, controller: urlController,
keyboardType: TextInputType.url, keyboardType: TextInputType.url,
onSubmitted: (value) { onSubmitted: (value) {
@ -112,94 +108,95 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
if (url.scheme.isEmpty) { if (url.scheme.isEmpty) {
url = Uri.parse("https://www.google.com/search?q=" + value); url = Uri.parse("https://www.google.com/search?q=" + value);
} }
webViewController?.loadUrl( webViewController?.loadUrl(urlRequest: URLRequest(url: url));
urlRequest: URLRequest(url: url));
}, },
), ),
Expanded( Expanded(
child: Stack( child: Stack(
children: [ children: [
InAppWebView( InAppWebView(
key: webViewKey, key: webViewKey,
// contextMenu: contextMenu, // contextMenu: contextMenu,
initialUrlRequest: initialUrlRequest: URLRequest(
URLRequest(url: Uri.parse("https://github.com/flutter")), url: Uri.parse("https://mdn.github.io/sw-test/")),
// initialFile: "assets/index.html", // initialFile: "assets/index.html",
initialUserScripts: UnmodifiableListView<UserScript>([]), initialUserScripts: UnmodifiableListView<UserScript>([]),
initialOptions: options, initialOptions: options,
pullToRefreshController: pullToRefreshController, pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
webViewController = controller; webViewController = controller;
}, },
onLoadStart: (controller, url) { onLoadStart: (controller, url) {
setState(() { setState(() {
this.url = url.toString(); this.url = url.toString();
urlController.text = this.url; urlController.text = this.url;
}); });
}, },
androidOnPermissionRequest: (controller, origin, resources) async { androidOnPermissionRequest:
return PermissionRequestResponse( (controller, origin, resources) async {
resources: resources, return PermissionRequestResponse(
action: PermissionRequestResponseAction.GRANT); resources: resources,
}, action: PermissionRequestResponseAction.GRANT);
shouldOverrideUrlLoading: (controller, navigationAction) async { },
var uri = navigationAction.request.url!; shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (![ if (![
"http", "http",
"https", "https",
"file", "file",
"chrome", "chrome",
"data", "data",
"javascript", "javascript",
"about" "about"
].contains(uri.scheme)) { ].contains(uri.scheme)) {
if (await canLaunch(url)) { if (await canLaunch(url)) {
// Launch the App // Launch the App
await launch( await launch(
url, url,
); );
// and cancel the request // and cancel the request
return NavigationActionPolicy.CANCEL; return NavigationActionPolicy.CANCEL;
}
} }
}
return NavigationActionPolicy.ALLOW; return NavigationActionPolicy.ALLOW;
}, },
onLoadStop: (controller, url) async { onLoadStop: (controller, url) async {
pullToRefreshController.endRefreshing();
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onLoadError: (controller, url, code, message) {
pullToRefreshController.endRefreshing();
},
onProgressChanged: (controller, progress) {
if (progress == 100) {
pullToRefreshController.endRefreshing(); pullToRefreshController.endRefreshing();
setState(() { }
this.url = url.toString(); setState(() {
urlController.text = this.url; this.progress = progress / 100;
}); urlController.text = this.url;
}, });
onLoadError: (controller, url, code, message) { },
pullToRefreshController.endRefreshing(); onUpdateVisitedHistory: (controller, url, androidIsReload) {
}, setState(() {
onProgressChanged: (controller, progress) { this.url = url.toString();
if (progress == 100) { urlController.text = this.url;
pullToRefreshController.endRefreshing(); });
} },
setState(() { onConsoleMessage: (controller, consoleMessage) {
this.progress = progress / 100; print(consoleMessage);
urlController.text = this.url; },
}); ),
}, progress < 1.0
onUpdateVisitedHistory: (controller, url, androidIsReload) { ? LinearProgressIndicator(value: progress)
setState(() { : Container(),
this.url = url.toString(); ],
urlController.text = this.url; ),
});
},
onConsoleMessage: (controller, consoleMessage) {
print(consoleMessage);
},
),
progress < 1.0
? LinearProgressIndicator(value: progress)
: Container(),
],
),
), ),
ButtonBar( ButtonBar(
alignment: MainAxisAlignment.center, alignment: MainAxisAlignment.center,

View File

@ -31,12 +31,13 @@ Future main() async {
AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController serviceWorkerController =
AndroidServiceWorkerController.instance(); AndroidServiceWorkerController.instance();
serviceWorkerController.serviceWorkerClient = AndroidServiceWorkerClient( await serviceWorkerController
.setServiceWorkerClient(AndroidServiceWorkerClient(
shouldInterceptRequest: (request) async { shouldInterceptRequest: (request) async {
print(request); print(request);
return null; return null;
}, },
); ));
} }
} }

View File

@ -13,7 +13,25 @@ class AndroidServiceWorkerController {
static const MethodChannel _channel = const MethodChannel( static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller'); 'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller');
AndroidServiceWorkerClient? serviceWorkerClient; AndroidServiceWorkerClient? _serviceWorkerClient;
AndroidServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
@Deprecated("Use setServiceWorkerClient instead")
set serviceWorkerClient(AndroidServiceWorkerClient? value) {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('isNull', () => value == null);
_channel.invokeMethod("setServiceWorkerClient", args);
_serviceWorkerClient = value;
}
///Sets the service worker client
setServiceWorkerClient(AndroidServiceWorkerClient? value) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('isNull', () => value == null);
await _channel.invokeMethod("setServiceWorkerClient", args);
_serviceWorkerClient = value;
}
///Gets the [AndroidServiceWorkerController] shared instance. ///Gets the [AndroidServiceWorkerController] shared instance.
static AndroidServiceWorkerController instance() { static AndroidServiceWorkerController instance() {

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.4.0+2 version: 5.4.0+3
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment: