From ce6e9066ba3f4ea4d7d0e0077af97cfb942c2174 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 22 Apr 2022 16:24:41 +0200 Subject: [PATCH 1/2] Fixed Android error in some cases when calling setServiceWorkerClient java method on ServiceWorkerManager initialization --- CHANGELOG.md | 4 + README.md | 2 - .../ServiceWorkerManager.java | 104 +++-- .../webview_flutter_test.dart | 408 ++++++++++-------- .../lib/in_app_browser_example.screen.dart | 4 +- example/lib/in_app_webiew_example.screen.dart | 173 ++++---- example/lib/main.dart | 5 +- .../android/service_worker_controller.dart | 20 +- pubspec.yaml | 2 +- 9 files changed, 409 insertions(+), 313 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7db1649..24a8cd27 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 - Fixed Android `ChromeCustomTabsActivity` not responding to the `ActionBroadcastReceiver` diff --git a/README.md b/README.md index 807cd3f0..3b0f4b04 100755 --- a/README.md +++ b/README.md @@ -59,6 +59,4 @@ Add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](https://f ## 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! \ No newline at end of file diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java index 804af946..aab8f3a2 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java @@ -38,50 +38,6 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { channel.setMethodCallHandler(this); if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) { serviceWorkerController = ServiceWorkerControllerCompat.getInstance(); - serviceWorkerController.setServiceWorkerClient(new ServiceWorkerClientCompat() { - @Nullable - @Override - public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) { - final Map 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 res = (Map) flutterResult.result; - String contentType = (String) res.get("contentType"); - String contentEncoding = (String) res.get("contentEncoding"); - byte[] data = (byte[]) res.get("data"); - Map responseHeaders = (Map) 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 { serviceWorkerController = null; } @@ -92,6 +48,13 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null; switch (call.method) { + case "setServiceWorkerClient": + { + Boolean isNull = (Boolean) call.argument("isNull"); + setServiceWorkerClient(isNull); + } + result.success(true); + break; case "getAllowContentAccess": if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) { result.success(serviceWorkerWebSettings.getAllowContentAccess()); @@ -152,9 +115,62 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { result.notImplemented(); } } + + private void setServiceWorkerClient(Boolean isNull) { + if (serviceWorkerController != null) { + serviceWorkerController.setServiceWorkerClient(isNull ? null : new ServiceWorkerClientCompat() { + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) { + final Map 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 res = (Map) flutterResult.result; + String contentType = (String) res.get("contentType"); + String contentEncoding = (String) res.get("contentEncoding"); + byte[] data = (byte[]) res.get("data"); + Map responseHeaders = (Map) 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() { channel.setMethodCallHandler(null); + if (serviceWorkerController != null) { + serviceWorkerController.setServiceWorkerClient(null); + serviceWorkerController = null; + } plugin = null; } } diff --git a/example/integration_test/webview_flutter_test.dart b/example/integration_test/webview_flutter_test.dart index e4a01c10..7d011eb3 100644 --- a/example/integration_test/webview_flutter_test.dart +++ b/example/integration_test/webview_flutter_test.dart @@ -77,7 +77,7 @@ void main() { AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true); } - group('InAppWebView', () { + group('InAppWebView', () { testWidgets('initialUrlRequest', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); await tester.pumpWidget( @@ -1709,13 +1709,14 @@ void main() { group('intercept ajax request', () { testWidgets('send string data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = - Completer(); + Completer(); final Completer> onAjaxReadyStateChangeCompleter = - Completer>(); + Completer>(); final Completer> onAjaxProgressCompleter = - Completer>(); + Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1745,9 +1746,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptAjaxRequest: true, - )), + clearCache: true, + useShouldInterceptAjaxRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -1780,9 +1781,9 @@ void main() { await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = - await onAjaxReadyStateChangeCompleter.future; + await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = - await onAjaxProgressCompleter.future; + await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, @@ -1795,13 +1796,14 @@ void main() { }); testWidgets('send json data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = - Completer(); + Completer(); final Completer> onAjaxReadyStateChangeCompleter = - Completer>(); + Completer>(); final Completer> onAjaxProgressCompleter = - Completer>(); + Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1835,15 +1837,16 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptAjaxRequest: true, - )), + clearCache: true, + useShouldInterceptAjaxRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, shouldInterceptAjaxRequest: (controller, ajaxRequest) async { 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.data = '{"firstname": "Foo2", "lastname": "Bar2"}'; @@ -1871,9 +1874,9 @@ void main() { await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = - await onAjaxReadyStateChangeCompleter.future; + await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = - await onAjaxProgressCompleter.future; + await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, @@ -1886,13 +1889,14 @@ void main() { }); testWidgets('send URLSearchParams data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = - Completer(); + Completer(); final Completer> onAjaxReadyStateChangeCompleter = - Completer>(); + Completer>(); final Completer> onAjaxProgressCompleter = - Completer>(); + Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1924,9 +1928,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptAjaxRequest: true, - )), + clearCache: true, + useShouldInterceptAjaxRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -1959,9 +1963,9 @@ void main() { await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = - await onAjaxReadyStateChangeCompleter.future; + await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = - await onAjaxProgressCompleter.future; + await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, @@ -1974,13 +1978,14 @@ void main() { }); testWidgets('send FormData', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer shouldInterceptAjaxPostRequestCompleter = - Completer(); + Completer(); final Completer> onAjaxReadyStateChangeCompleter = - Completer>(); + Completer>(); final Completer> onAjaxProgressCompleter = - Completer>(); + Completer>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2012,9 +2017,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptAjaxRequest: true, - )), + clearCache: true, + useShouldInterceptAjaxRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -2025,7 +2030,9 @@ void main() { var bodyString = String.fromCharCodes(body); 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'; shouldInterceptAjaxPostRequestCompleter.complete(controller); return ajaxRequest; @@ -2051,9 +2058,9 @@ void main() { await shouldInterceptAjaxPostRequestCompleter.future; final Map onAjaxReadyStateChangeValue = - await onAjaxReadyStateChangeCompleter.future; + await onAjaxReadyStateChangeCompleter.future; final Map onAjaxProgressValue = - await onAjaxProgressCompleter.future; + await onAjaxProgressCompleter.future; expect( mapEquals(onAjaxReadyStateChangeValue, @@ -2068,11 +2075,12 @@ void main() { group('intercept fetch request', () { testWidgets('send string data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer> fetchPostCompleter = - Completer>(); + Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = - Completer(); + Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2113,9 +2121,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptFetchRequest: true, - )), + clearCache: true, + useShouldInterceptFetchRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); @@ -2147,11 +2155,12 @@ void main() { }); testWidgets('send json data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer> fetchPostCompleter = - Completer>(); + Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = - Completer(); + Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2196,9 +2205,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptFetchRequest: true, - )), + clearCache: true, + useShouldInterceptFetchRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); @@ -2211,7 +2220,8 @@ void main() { }, shouldInterceptFetchRequest: (controller, fetchRequest) async { 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"}'; shouldInterceptFetchPostRequestCompleter.complete(); @@ -2231,11 +2241,12 @@ void main() { }); testWidgets('send URLSearchParams data', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer> fetchPostCompleter = - Completer>(); + Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = - Completer(); + Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2278,9 +2289,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptFetchRequest: true, - )), + clearCache: true, + useShouldInterceptFetchRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); @@ -2312,11 +2323,12 @@ void main() { }); testWidgets('send FormData', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer> fetchPostCompleter = - Completer>(); + Completer>(); final Completer shouldInterceptFetchPostRequestCompleter = - Completer(); + Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2357,9 +2369,9 @@ void main() { """), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - clearCache: true, - useShouldInterceptFetchRequest: true, - )), + clearCache: true, + useShouldInterceptFetchRequest: true, + )), onWebViewCreated: (controller) { controllerCompleter.complete(controller); @@ -2377,7 +2389,9 @@ void main() { var bodyString = String.fromCharCodes(body); 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(); return fetchRequest; }, @@ -2404,24 +2418,24 @@ void main() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: - URLRequest(url: Uri.parse('https://flutter.dev/')), + URLRequest(url: Uri.parse('https://flutter.dev/')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, initialOptions: InAppWebViewGroupOptions( crossPlatform: - InAppWebViewOptions(clearCache: true, contentBlockers: [ - ContentBlocker( - trigger: + InAppWebViewOptions(clearCache: true, contentBlockers: [ + ContentBlocker( + trigger: ContentBlockerTrigger(urlFilter: ".*", resourceType: [ - ContentBlockerTriggerResourceType.IMAGE, - ContentBlockerTriggerResourceType.STYLE_SHEET - ], ifTopUrl: [ - "https://flutter.dev/" - ]), - action: ContentBlockerAction( - type: ContentBlockerActionType.BLOCK)) - ])), + ContentBlockerTriggerResourceType.IMAGE, + ContentBlockerTriggerResourceType.STYLE_SHEET + ], ifTopUrl: [ + "https://flutter.dev/" + ]), + action: ContentBlockerAction( + type: ContentBlockerActionType.BLOCK)) + ])), onLoadStop: (controller, url) { pageLoaded.complete(); }, @@ -3261,7 +3275,7 @@ setTimeout(function() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: - URLRequest(url: Uri.parse('https://github.com/flutter')), + URLRequest(url: Uri.parse('https://github.com/flutter')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -3278,7 +3292,7 @@ setTimeout(function() { ); final InAppWebViewController controller = - await controllerCompleter.future; + await controllerCompleter.future; await pageLoaded.future; listenForScaleChange = true; @@ -4167,10 +4181,14 @@ setTimeout(function() { await pageLoaded.future; await controller.injectJavascriptFileFromUrl( - urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'), - scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery-error', onError: () { - jQueryLoadError.complete(); - },)); + urlFile: + Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'), + scriptHtmlTagAttributes: ScriptHtmlTagAttributes( + id: 'jquery-error', + onError: () { + jQueryLoadError.complete(); + }, + )); await jQueryLoadError.future; expect( await controller.evaluateJavascript( @@ -4182,9 +4200,12 @@ setTimeout(function() { await controller.injectJavascriptFileFromUrl( urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'), - scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery', onLoad: () { - jQueryLoaded.complete(); - },)); + scriptHtmlTagAttributes: ScriptHtmlTagAttributes( + id: 'jquery', + onLoad: () { + jQueryLoaded.complete(); + }, + )); await jQueryLoaded.future; expect( await controller.evaluateJavascript( @@ -4903,21 +4924,19 @@ setTimeout(function() { 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(); final pullToRefreshController = PullToRefreshController( options: PullToRefreshOptions( - color: Colors.blue, - size: AndroidPullToRefreshSize.DEFAULT, - backgroundColor: Colors.grey, - enabled: true, - slingshotDistance: 150, - distanceToTriggerSync: 150, - attributedTitle: IOSNSAttributedString(string: "test") - ), - onRefresh: () { - - }, + color: Colors.blue, + size: AndroidPullToRefreshSize.DEFAULT, + backgroundColor: Colors.grey, + enabled: true, + slingshotDistance: 150, + distanceToTriggerSync: 150, + attributedTitle: IOSNSAttributedString(string: "test")), + onRefresh: () {}, ); await tester.pumpWidget( @@ -4925,12 +4944,11 @@ setTimeout(function() { textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), - initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')), + initialUrlRequest: + URLRequest(url: Uri.parse('https://github.com/flutter')), initialOptions: InAppWebViewGroupOptions( - android: AndroidInAppWebViewOptions( - useHybridComposition: true - ) - ), + android: + AndroidInAppWebViewOptions(useHybridComposition: true)), pullToRefreshController: pullToRefreshController, onWebViewCreated: (controller) { controllerCompleter.complete(controller); @@ -4939,22 +4957,22 @@ setTimeout(function() { ), ); final InAppWebViewController controller = - await controllerCompleter.future; + await controllerCompleter.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'https://github.com/flutter'); }); group('WebMessage', () { testWidgets('WebMessageChannel', (WidgetTester tester) async { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer webMessageCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), - initialData: InAppWebViewInitialData( - data: """ + initialData: InAppWebViewInitialData(data: """ @@ -4989,15 +5007,20 @@ setTimeout(function() { webMessageCompleter.complete(consoleMessage.message); }, onLoadStop: (controller, url) async { - var webMessageChannel = await controller.createWebMessageChannel(); + var webMessageChannel = + await controller.createWebMessageChannel(); var port1 = webMessageChannel!.port1; var port2 = webMessageChannel.port2; 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.evaluateJavascript(source: "document.getElementById('button').click();"); + await controller.postWebMessage( + 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 { - final Completer controllerCompleter = Completer(); + final Completer controllerCompleter = + Completer(); final Completer pageLoaded = Completer(); final Completer webMessageCompleter = Completer(); await tester.pumpWidget( @@ -5021,8 +5045,10 @@ setTimeout(function() { await controller.addWebMessageListener(WebMessageListener( jsObjectName: "myTestObj", allowedOriginRules: Set.from(["https://*.example.com"]), - onPostMessage: (message, sourceOrigin, isMainFrame, replyProxy) { - assert(sourceOrigin.toString() == "https://www.example.com"); + onPostMessage: + (message, sourceOrigin, isMainFrame, replyProxy) { + assert( + sourceOrigin.toString() == "https://www.example.com"); assert(isMainFrame); replyProxy.postMessage(message! + " and back"); @@ -5042,7 +5068,8 @@ setTimeout(function() { ), ); 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 controller.evaluateJavascript(source: """ @@ -5273,7 +5300,9 @@ setTimeout(function() { }, skip: !Platform.isAndroid); test('setWebContentsDebuggingEnabled', () async { - expect(AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true), completes); + expect( + AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true), + completes); }, skip: !Platform.isAndroid); }, skip: !Platform.isAndroid); @@ -5377,8 +5406,7 @@ setTimeout(function() { textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), - initialData: InAppWebViewInitialData( - data: """ + initialData: InAppWebViewInitialData(data: """ @@ -5393,8 +5421,7 @@ setTimeout(function() { - """ - ), + """), initialOptions: InAppWebViewGroupOptions( ios: IOSInAppWebViewOptions( applePayAPIEnabled: true, @@ -5421,6 +5448,47 @@ setTimeout(function() { }, 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', () { testWidgets('set, get, delete', (WidgetTester tester) async { CookieManager cookieManager = CookieManager.instance(); @@ -5474,7 +5542,8 @@ setTimeout(function() { final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), + initialUrlRequest: + URLRequest(url: Uri.parse("https://github.com/flutter")), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -5503,20 +5572,20 @@ setTimeout(function() { final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), - onWebViewCreated: (controller) { - controllerCompleter.complete(controller); - }, - onLoadStop: (controller, url) async { - pageLoaded.complete(); - } - ); + initialUrlRequest: + URLRequest(url: Uri.parse("https://github.com/flutter")), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }); await headlessWebView.run(); expect(headlessWebView.isRunning(), true); final InAppWebViewController controller = - await controllerCompleter.future; + await controllerCompleter.future; await pageLoaded.future; final String? url = (await controller.getUrl())?.toString(); @@ -5537,11 +5606,12 @@ setTimeout(function() { final Completer controllerCompleter = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), - initialSize: Size(600, 800), - onWebViewCreated: (controller) { - controllerCompleter.complete(controller); - }, + initialUrlRequest: + URLRequest(url: Uri.parse("https://github.com/flutter")), + initialSize: Size(600, 800), + onWebViewCreated: (controller) { + controllerCompleter.complete(controller); + }, ); await headlessWebView.run(); @@ -5566,7 +5636,8 @@ setTimeout(function() { final Completer pageLoaded = Completer(); var headlessWebView = new HeadlessInAppWebView( - initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")), + initialUrlRequest: + URLRequest(url: Uri.parse("https://github.com/flutter")), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)), onWebViewCreated: (controller) { @@ -5610,8 +5681,7 @@ setTimeout(function() { expect(inAppBrowser.isOpened(), true); expect(() async { await inAppBrowser.openUrlRequest( - urlRequest: - URLRequest(url: Uri.parse("https://flutter.dev"))); + urlRequest: URLRequest(url: Uri.parse("https://flutter.dev"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; @@ -5633,13 +5703,14 @@ setTimeout(function() { await inAppBrowser.show(); }, throwsA(isInstanceOf())); - 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; expect(inAppBrowser.isOpened(), true); expect(() async { await inAppBrowser.openUrlRequest( urlRequest: - URLRequest(url: Uri.parse("https://github.com/flutter"))); + URLRequest(url: Uri.parse("https://github.com/flutter"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; @@ -5661,7 +5732,8 @@ setTimeout(function() { await inAppBrowser.show(); }, throwsA(isInstanceOf())); - await inAppBrowser.openData(data: """ + await inAppBrowser.openData( + data: """ @@ -5685,7 +5757,7 @@ setTimeout(function() { expect(() async { await inAppBrowser.openUrlRequest( urlRequest: - URLRequest(url: Uri.parse("https://github.com/flutter"))); + URLRequest(url: Uri.parse("https://github.com/flutter"))); }, throwsA(isInstanceOf())); await inAppBrowser.firstPageLoaded.future; @@ -5728,12 +5800,12 @@ setTimeout(function() { var chromeSafariBrowser = new MyChromeSafariBrowser(); 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; expect(chromeSafariBrowser.isOpened(), true); expect(() async { - await chromeSafariBrowser.open( - url: Uri.parse("https://flutter.dev")); + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); @@ -5748,21 +5820,18 @@ setTimeout(function() { expect(chromeSafariBrowser.isOpened(), false); await chromeSafariBrowser.open( - url: Uri.parse("https://github.com/flutter"), - options: ChromeSafariBrowserClassOptions( - android: AndroidChromeCustomTabsOptions( - isSingleInstance: true - ) - ) - ); + url: Uri.parse("https://github.com/flutter"), + options: ChromeSafariBrowserClassOptions( + android: + AndroidChromeCustomTabsOptions(isSingleInstance: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { - await chromeSafariBrowser.open( - url: Uri.parse("https://flutter.dev")); + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); - await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); + await expectLater( + chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); @@ -5776,18 +5845,15 @@ setTimeout(function() { url: Uri.parse("https://github.com/flutter"), options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions( - isTrustedWebActivity: true - ) - ) - ); + isTrustedWebActivity: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { - await chromeSafariBrowser.open( - url: Uri.parse("https://flutter.dev")); + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); - await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); + await expectLater( + chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); @@ -5801,19 +5867,15 @@ setTimeout(function() { url: Uri.parse("https://github.com/flutter"), options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions( - isTrustedWebActivity: true, - isSingleInstance: true - ) - ) - ); + isTrustedWebActivity: true, isSingleInstance: true))); await chromeSafariBrowser.browserCreated.future; expect(chromeSafariBrowser.isOpened(), true); expect(() async { - await chromeSafariBrowser.open( - url: Uri.parse("https://flutter.dev")); + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); }, throwsA(isInstanceOf())); - await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); + await expectLater( + chromeSafariBrowser.firstPageLoaded.future, completes); await chromeSafariBrowser.close(); await chromeSafariBrowser.browserClosed.future; expect(chromeSafariBrowser.isOpened(), false); @@ -5837,8 +5899,8 @@ setTimeout(function() { textDirection: TextDirection.ltr, child: InAppWebView( key: GlobalKey(), - initialUrlRequest: - URLRequest(url: Uri.parse('http://localhost:8080/test_assets/index.html')), + initialUrlRequest: URLRequest( + url: Uri.parse('http://localhost:8080/test_assets/index.html')), onWebViewCreated: (controller) { controllerCompleter.complete(controller); }, @@ -5846,7 +5908,7 @@ setTimeout(function() { ), ); final InAppWebViewController controller = - await controllerCompleter.future; + await controllerCompleter.future; final String? currentUrl = (await controller.getUrl())?.toString(); expect(currentUrl, 'http://localhost:8080/test_assets/index.html'); }); diff --git a/example/lib/in_app_browser_example.screen.dart b/example/lib/in_app_browser_example.screen.dart index f8e8e7eb..86b09622 100755 --- a/example/lib/in_app_browser_example.screen.dart +++ b/example/lib/in_app_browser_example.screen.dart @@ -83,7 +83,6 @@ class InAppBrowserExampleScreen extends StatefulWidget { } class _InAppBrowserExampleScreenState extends State { - late PullToRefreshController pullToRefreshController; @override @@ -99,7 +98,8 @@ class _InAppBrowserExampleScreenState extends State { widget.browser.webViewController.reload(); } else if (Platform.isIOS) { widget.browser.webViewController.loadUrl( - urlRequest: URLRequest(url: await widget.browser.webViewController.getUrl())); + urlRequest: URLRequest( + url: await widget.browser.webViewController.getUrl())); } }, ); diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 90f77064..fff25346 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -17,15 +17,13 @@ class InAppWebViewExampleScreen extends StatefulWidget { } class _InAppWebViewExampleScreenState extends State { - final GlobalKey webViewKey = GlobalKey(); InAppWebViewController? webViewController; InAppWebViewGroupOptions options = InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( - useShouldOverrideUrlLoading: true, - mediaPlaybackRequiresUserGesture: false - ), + useShouldOverrideUrlLoading: true, + mediaPlaybackRequiresUserGesture: false), android: AndroidInAppWebViewOptions( useHybridComposition: true, ), @@ -102,9 +100,7 @@ class _InAppWebViewExampleScreenState extends State { body: SafeArea( child: Column(children: [ TextField( - decoration: InputDecoration( - prefixIcon: Icon(Icons.search) - ), + decoration: InputDecoration(prefixIcon: Icon(Icons.search)), controller: urlController, keyboardType: TextInputType.url, onSubmitted: (value) { @@ -112,94 +108,95 @@ class _InAppWebViewExampleScreenState extends State { if (url.scheme.isEmpty) { url = Uri.parse("https://www.google.com/search?q=" + value); } - webViewController?.loadUrl( - urlRequest: URLRequest(url: url)); + webViewController?.loadUrl(urlRequest: URLRequest(url: url)); }, ), Expanded( - child: Stack( - children: [ - InAppWebView( - key: webViewKey, - // contextMenu: contextMenu, - initialUrlRequest: - URLRequest(url: Uri.parse("https://github.com/flutter")), - // initialFile: "assets/index.html", - initialUserScripts: UnmodifiableListView([]), - initialOptions: options, - pullToRefreshController: pullToRefreshController, - onWebViewCreated: (controller) { - webViewController = controller; - }, - onLoadStart: (controller, url) { - setState(() { - this.url = url.toString(); - urlController.text = this.url; - }); - }, - androidOnPermissionRequest: (controller, origin, resources) async { - return PermissionRequestResponse( - resources: resources, - action: PermissionRequestResponseAction.GRANT); - }, - shouldOverrideUrlLoading: (controller, navigationAction) async { - var uri = navigationAction.request.url!; + child: Stack( + children: [ + InAppWebView( + key: webViewKey, + // contextMenu: contextMenu, + initialUrlRequest: URLRequest( + url: Uri.parse("https://mdn.github.io/sw-test/")), + // initialFile: "assets/index.html", + initialUserScripts: UnmodifiableListView([]), + initialOptions: options, + pullToRefreshController: pullToRefreshController, + onWebViewCreated: (controller) { + webViewController = controller; + }, + onLoadStart: (controller, url) { + setState(() { + this.url = url.toString(); + urlController.text = this.url; + }); + }, + androidOnPermissionRequest: + (controller, origin, resources) async { + return PermissionRequestResponse( + resources: resources, + action: PermissionRequestResponseAction.GRANT); + }, + shouldOverrideUrlLoading: + (controller, navigationAction) async { + var uri = navigationAction.request.url!; - if (![ - "http", - "https", - "file", - "chrome", - "data", - "javascript", - "about" - ].contains(uri.scheme)) { - if (await canLaunch(url)) { - // Launch the App - await launch( - url, - ); - // and cancel the request - return NavigationActionPolicy.CANCEL; - } + if (![ + "http", + "https", + "file", + "chrome", + "data", + "javascript", + "about" + ].contains(uri.scheme)) { + if (await canLaunch(url)) { + // Launch the App + await launch( + url, + ); + // and cancel the request + return NavigationActionPolicy.CANCEL; } + } - return NavigationActionPolicy.ALLOW; - }, - onLoadStop: (controller, url) async { + return NavigationActionPolicy.ALLOW; + }, + 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(); - setState(() { - this.url = url.toString(); - urlController.text = this.url; - }); - }, - onLoadError: (controller, url, code, message) { - pullToRefreshController.endRefreshing(); - }, - onProgressChanged: (controller, progress) { - if (progress == 100) { - pullToRefreshController.endRefreshing(); - } - setState(() { - this.progress = progress / 100; - urlController.text = this.url; - }); - }, - onUpdateVisitedHistory: (controller, url, androidIsReload) { - setState(() { - this.url = url.toString(); - urlController.text = this.url; - }); - }, - onConsoleMessage: (controller, consoleMessage) { - print(consoleMessage); - }, - ), - progress < 1.0 - ? LinearProgressIndicator(value: progress) - : Container(), - ], - ), + } + setState(() { + this.progress = progress / 100; + urlController.text = this.url; + }); + }, + onUpdateVisitedHistory: (controller, url, androidIsReload) { + setState(() { + this.url = url.toString(); + urlController.text = this.url; + }); + }, + onConsoleMessage: (controller, consoleMessage) { + print(consoleMessage); + }, + ), + progress < 1.0 + ? LinearProgressIndicator(value: progress) + : Container(), + ], + ), ), ButtonBar( alignment: MainAxisAlignment.center, diff --git a/example/lib/main.dart b/example/lib/main.dart index 895042e5..3d9154cc 100755 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -31,12 +31,13 @@ Future main() async { AndroidServiceWorkerController serviceWorkerController = AndroidServiceWorkerController.instance(); - serviceWorkerController.serviceWorkerClient = AndroidServiceWorkerClient( + await serviceWorkerController + .setServiceWorkerClient(AndroidServiceWorkerClient( shouldInterceptRequest: (request) async { print(request); return null; }, - ); + )); } } diff --git a/lib/src/android/service_worker_controller.dart b/lib/src/android/service_worker_controller.dart index 2d364e45..1c51fe06 100644 --- a/lib/src/android/service_worker_controller.dart +++ b/lib/src/android/service_worker_controller.dart @@ -13,7 +13,25 @@ class AndroidServiceWorkerController { static const MethodChannel _channel = const MethodChannel( 'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller'); - AndroidServiceWorkerClient? serviceWorkerClient; + AndroidServiceWorkerClient? _serviceWorkerClient; + + AndroidServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient; + + @Deprecated("Use setServiceWorkerClient instead") + set serviceWorkerClient(AndroidServiceWorkerClient? value) { + Map args = {}; + args.putIfAbsent('isNull', () => value == null); + _channel.invokeMethod("setServiceWorkerClient", args); + _serviceWorkerClient = value; + } + + ///Sets the service worker client + setServiceWorkerClient(AndroidServiceWorkerClient? value) async { + Map args = {}; + args.putIfAbsent('isNull', () => value == null); + await _channel.invokeMethod("setServiceWorkerClient", args); + _serviceWorkerClient = value; + } ///Gets the [AndroidServiceWorkerController] shared instance. static AndroidServiceWorkerController instance() { diff --git a/pubspec.yaml b/pubspec.yaml index 3191400f..359bb63c 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ 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. -version: 5.4.0+2 +version: 5.4.0+3 homepage: https://github.com/pichillilorenzo/flutter_inappwebview environment: From d1e4dc55d0e7847c65cc2ad64f79d86d99e3742a Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 22 Apr 2022 16:32:03 +0200 Subject: [PATCH 2/2] updated example --- example/lib/in_app_webiew_example.screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index fff25346..b656d6fe 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -117,8 +117,8 @@ class _InAppWebViewExampleScreenState extends State { InAppWebView( key: webViewKey, // contextMenu: contextMenu, - initialUrlRequest: URLRequest( - url: Uri.parse("https://mdn.github.io/sw-test/")), + initialUrlRequest: + URLRequest(url: Uri.parse("https://github.com/flutter")), // initialFile: "assets/index.html", initialUserScripts: UnmodifiableListView([]), initialOptions: options,