pichillilorenzo / flutter_inappwebview

A Flutter plugin that allows you to add an inline webview, to use a headless webview, and to open an in-app browser window.
https://inappwebview.dev
Apache License 2.0
3.18k stars 1.54k forks source link

App crashing with "supportMultipleWindows:true" #388

Open rajeshzmoke opened 4 years ago

rajeshzmoke commented 4 years ago

Environment

[✓] Flutter (Channel stable, v1.17.3, on Mac OS X 10.14.6 18G103, locale en-IN)
    • Flutter version 1.17.3 at /Users/dhruv/Desktop/flutter
    • Framework revision b041144f83 (5 days ago), 2020-06-04 09:26:11 -0700
    • Engine revision ee76268252
    • Dart version 2.8.4

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    • Android SDK at /Users/dhruv/Library/Android/sdk
    • Platform android-29, build-tools 29.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.3.1, Build version 11C505
    • CocoaPods version 1.8.4

[✓] Android Studio (version 3.6)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 45.1.1
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[✓] VS Code (version 1.45.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.11.0

[✓] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

• No issues found!

flutter_inappwebview: ^3.3.0+3

Description

App crashing when clicking on a link that opens in a new tab, Expected behavior: loading inital url and then when clicking on a button in the webview open a new new window/ tab

Current behavior: loading initial url and when clicking on a button in the webview crashes the app

Steps to reproduce

InAppWebView(
        key: widget.key,
        initialOptions: InAppWebViewGroupOptions(
            android: AndroidInAppWebViewOptions(
              supportMultipleWindows: true,
            ),
            crossPlatform: InAppWebViewOptions(
              debuggingEnabled: true,
              javaScriptEnabled: true,
              useShouldOverrideUrlLoading: true,
            )),
        onCreateWindow: (controller, onCreateWindowRequest) async {
          print(onCreateWindowRequest);
          controller.loadUrl(url: onCreateWindowRequest.url);

          // await launch(onCreateWindowRequest.url);

          return true;
        },
        shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async {
          return ShouldOverrideUrlLoadingAction.ALLOW;
        },
        onWebViewCreated: (InAppWebViewController controller) {
          if (mounted) {
            setCookie(controller);
          }
        },
        onLoadError: (
          InAppWebViewController controller,
          String url,
          int code,
          String message,
        ) {
          print(message);
        },
        onLoadHttpError: (
          InAppWebViewController controller,
          String url,
          int code,
          String message,
        ) {
          print(message);
        },
        onProgressChanged: (InAppWebViewController controller, int progress) async {
          if (mounted) {

            if (progress > 50) {
              controller.injectCSSCode(source: ".sn-l__navBar-wrapper{display:none};");
            }
          }
        },
        onLoadStart: (InAppWebViewController controller, String url) {
          if (mounted) {
            if (url.startsWith(widget.url)) {
            }
          }
        },
        onLoadStop: (InAppWebViewController controller, String url) {
          if (mounted) {
            controller.injectCSSCode(source: ".sn-l__navBar-wrapper{display:none};");
          }
        },
      )

Stacktrace/Logcat

System.err(20202): java.lang.NullPointerException: Attempt to read from field 'android.view.WindowManager$LayoutParams android.view.ViewRootImpl.mWindowAttributes' on a null object reference
W/System.err(20202):    at android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:1625)
W/System.err(20202):    at android.view.inputmethod.InputMethodManager.checkFocus(InputMethodManager.java:1864)
W/System.err(20202):    at android.view.inputmethod.InputMethodManager.isActive(InputMethodManager.java:1183)
W/System.err(20202):    at LM.a(PG:2)
W/System.err(20202):    at org.chromium.content.browser.input.ImeAdapterImpl.n(PG:230)
W/System.err(20202):    at org.chromium.content.browser.input.ImeAdapterImpl.f(PG:280)
W/System.err(20202):    at org.chromium.content.browser.input.ImeAdapterImpl.onConnectedToRenderProcess(PG:481)
W/System.err(20202):    at org.chromium.content.browser.framehost.NavigationControllerImpl.nativeLoadUrl(Native Method)
W/System.err(20202):    at org.chromium.content.browser.framehost.NavigationControllerImpl.a(PG:67)
W/System.err(20202):    at org.chromium.android_webview.AwContents.b(PG:602)
W/System.err(20202):    at org.chromium.android_webview.AwContents.a(PG:437)
W/System.err(20202):    at org.chromium.android_webview.AwContents.b(PG:442)
W/System.err(20202):    at com.android.webview.chromium.WebViewChromium.loadUrl(PG:251)
W/System.err(20202):    at android.webkit.WebView.loadUrl(WebView.java:727)
W/System.err(20202):    at io.flutter.plugins.webviewflutter.FlutterWebView.<init>(FlutterWebView.java:113)
W/System.err(20202):    at io.flutter.plugins.webviewflutter.WebViewFactory.create(WebViewFactory.java:29)
W/System.err(20202):    at io.flutter.plugin.platform.SingleViewPresentation.onCreate(SingleViewPresentation.java:179)
W/System.err(20202):    at android.app.Dialog.dispatchOnCreate(Dialog.java:421)
W/System.err(20202):    at android.app.Dialog.show(Dialog.java:315)
W/System.err(20202):    at android.app.Presentation.show(Presentation.java:250)
W/System.err(20202):    at io.flutter.plugin.platform.VirtualDisplayController.<init>(VirtualDisplayController.java:94)
W/System.err(20202):    at io.flutter.plugin.platform.VirtualDisplayController.create(VirtualDisplayController.java:47)
W/System.err(20202):    at io.flutter.plugin.platform.PlatformViewsController$1.createPlatformView(PlatformViewsController.java:112)
W/System.err(20202):    at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.create(PlatformViewsChannel.java:95)
W/System.err(20202):    at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.onMethodCall(PlatformViewsChannel.java:59)
W/System.err(20202):    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:226)
W/System.err(20202):    at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
W/System.err(20202):    at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:631)
W/System.err(20202):    at android.os.MessageQueue.nativePollOnce(Native Method)
W/System.err(20202):    at android.os.MessageQueue.next(MessageQueue.java:336)
W/System.err(20202):    at android.os.Looper.loop(Looper.java:174)
W/System.err(20202):    at android.app.ActivityThread.main(ActivityThread.java:7356)
W/System.err(20202):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err(20202):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
W/System.err(20202):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
D/EGL_emulation(20202): eglMakeCurrent: 0xb1657fc0: ver 3 0 (tinfo 0xbda1ed10)
E/eglCodecCommon(20202): glUtilsParamSize: unknow param 0x000088ef
E/eglCodecCommon(20202): glUtilsParamSize: unknow param 0x000088ef
D/eglCodecCommon(20202): setVertexArrayObject: set vao to 5 (5) 0 0
F/chromium(20202): [FATAL:jni_android.cc(249)] Please include Java exception stack in crash report
F/libc    (20202): Fatal signal 5 (SIGTRAP), code 128 (SI_KERNEL), fault addr 0x0 in tid 20202 (ensibull.mobile), pid 20202 (ensibull.mobile)
alex-min commented 4 years ago

I had the same bug and I found how it happens, it happens when the onCreateWindow.url is 'about:blank' if you do

  onCreateWindow: (controller, onCreateWindow) {
        controller.loadUrl(url: 'https://www.google.com');
      },

the error disappear, not sure now I can load the blank tab yet then though.

pichillilorenzo commented 4 years ago

.. open a new window/ tab ..

You need yourself to implement webview tab features, InAppWebView widget is not a browser, but a WebView. The onCreateWindow is just an event that intercepts this intent. If you want to open a new InAppWebView widget, you need to implement it yourself, like creating a browser app.

@alex-min I cannot reproduce your error. This example is working fine when I click on <a target="_blank" href="about:blank">TEST TEST TEST</a>:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: SafeArea(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  margin: const EdgeInsets.all(20.0),
                  decoration:
                  BoxDecoration(border: Border.all(color: Colors.blueAccent)),
                  child: InAppWebView(
                    initialData: InAppWebViewInitialData(
                        data: """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Flutter InAppWebView</title>
</head>
<body class="text-center">
    <a target="_blank" href="about:blank">TEST TEST TEST</a>
</body>
</html>
                      """
                    ),
                    initialHeaders: {},
                    initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                          useShouldOverrideUrlLoading: true,
                        ),
                        android: AndroidInAppWebViewOptions(
                            supportMultipleWindows: true
                        )
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      print("onWebViewCreated");
                      _webViewController = controller;
                    },
                    onCreateWindow: (controller, onCreateWindowRequest) async {
                      print("onCreateWindow called");
                      controller.loadUrl(url: onCreateWindowRequest.url);
                    },
                    onLoadStart: (InAppWebViewController controller, String url) {
                      print("onLoadStart $url");
                    },
                    onLoadStop: (InAppWebViewController controller, String url) async {
                      print("onLoadStop $url");
                    },
                  ),
                ),
              ),
            ]))
    );
  }
}

Could you share the URL that contains the link of your blank tab? @rajeshzmoke if you can, you too, otherwise I cannot debug this error.

Also, this error happens on all devices and in all cases? Thanks

alex-min commented 4 years ago

After more investigation, it does happen from some urls and not others, I'm not sure what causes it, google.com is totally fine but I was trying to integrate plaid.com and the plaid window is a 100% crash. You can try to navigate to this url with loadUrl: https://cdn.plaid.com/link/v2/stable/sandbox-oauth-login.html?redirect_uri=https%3A%2F%2Fcdn.plaid.com%2Flink%2Fv2%2Fstable%2Foauth.html&state=eyJvYXV0aF9zdGF0ZV9pZCI6Ijk0ZjJhN2RkLTVlYzMtNDYxNC1hZWQ4LWI0MDFiZjgyZWE5NiIsInVzZV9vYXV0aF9jYWxsYmFjayI6ZmFsc2UsInBsYWlkX2VudiI6InNhbmRib3gifQ. So I'm not sure what js api causes it, difficult to say.

alex-min commented 4 years ago

let me know if you can reproduce as well

pichillilorenzo commented 4 years ago

@alex-min This is working fine:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: SafeArea(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  child: InAppWebView(
                    initialData: InAppWebViewInitialData(
                        data: """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Flutter InAppWebView</title>
</head>
<body class="text-center">
    <a target="_blank" href="https://cdn.plaid.com/link/v2/stable/sandbox-oauth-login.html?redirect_uri=https%3A%2F%2Fcdn.plaid.com%2Flink%2Fv2%2Fstable%2Foauth.html&state=eyJvYXV0aF9zdGF0ZV9pZCI6Ijk0ZjJhN2RkLTVlYzMtNDYxNC1hZWQ4LWI0MDFiZjgyZWE5NiIsInVzZV9vYXV0aF9jYWxsYmFjayI6ZmFsc2UsInBsYWlkX2VudiI6InNhbmRib3gifQ">TEST TEST TEST</a>
</body>
</html>
                      """
                    ),
                    initialHeaders: {},
                    initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                          useShouldOverrideUrlLoading: true,
                        ),
                        android: AndroidInAppWebViewOptions(
                            supportMultipleWindows: true
                        )
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      print("onWebViewCreated");
                      _webViewController = controller;
                    },
                    onCreateWindow: (controller, onCreateWindowRequest) async {
                      print("onCreateWindow called with URL ${onCreateWindowRequest.url}");
                      controller.loadUrl(url: onCreateWindowRequest.url);
                    },
                    onLoadStart: (InAppWebViewController controller, String url) {
                      print("onLoadStart $url");
                    },
                    onLoadStop: (InAppWebViewController controller, String url) async {
                      print("onLoadStop $url");
                    },
                  ),
                ),
              ),
            ]))
    );
  }
}

test_on_create_window

alex-min commented 4 years ago

That also works for me, I will try to reduce it to a minimum test case

alex-min commented 4 years ago

here you go, if you replace the html by that you can trigger it

InAppWebViewInitialData(data: """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Flutter InAppWebView</title>
</head>
<body class="text-center">
    <a id="test">TEST TEST TEST</a>
    <script>
      document.getElementById('test').onclick = () => {
        let w = window.open('https://cdn.plaid.com/link/v2/stable/sandbox-oauth-login.html?redirect_uri=https%3A%2F%2Fcdn.plaid.com%2Flink%2Fv2%2Fstable%2Foauth.html&state=eyJvYXV0aF9zdGF0ZV9pZCI6Ijk0ZjJhN2RkLTVlYzMtNDYxNC1hZWQ4LWI0MDFiZjgyZWE5NiIsInVzZV9vYXV0aF9jYWxsYmFjayI6ZmFsc2UsInBsYWlkX2VudiI6InNhbmRib3gifQ', 'test');
        w.focus();
        setInterval(() => {
          console.log(w.closed);
          console.log(w.opener);
        }, 1000);
      }
    </script>
</body>
</html>
                      """)
pichillilorenzo commented 4 years ago

I tried it but I didn't have any error. Instead, I received about:blank as URL of onCreateWindow event. Using javascript is a little bit problematic.

So, I'm thinking to re-implement the onCreateWindow event in another way, adding also the onCloseWindow event (in android, for example, is the https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView) event). I need to think about how to make it work in Flutter.

alex-min commented 4 years ago

Ah interesting, there's maybe something different on my config, this sample triggers 100% of the time the crash on my end. Actually it seems to be more the .closed & .opener which seems to cause problems for me rather than the window.open(), I've added them because it's what plaid.com does in a setInterval (I've spied with object proxies to find that) and that what I think makes the page crash.

here is my flutter doctor if it can help you:

[✓] Flutter (Channel stable, v1.17.4, on Linux, locale fr_FR.UTF-8)
    • Flutter version 1.17.4 at /usr/share/flutter
    • Framework revision 1ad9baa8b9 (il y a 6 jours), 2020-06-17 14:41:16 -0700
    • Engine revision ee76268252
    • Dart version 2.8.4

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    • Android SDK at /home/alex/Android/Sdk
    • Platform android-30, build-tools 29.0.2
    • ANDROID_HOME = /home/alex/Android/Sdk
    • Java binary at: /home/alex/android/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Android Studio (version 4.0)
    • Android Studio at /home/alex/android/android-studio
    • Flutter plugin version 46.0.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

• No issues found!

Let me know if I can help you with any command, the traceback I have is identical of the one of @rajeshzmoke

rajeshzmoke commented 4 years ago

@alex-min @pichillilorenzo so what i've found is that when debugging in an android emulator i get an actual url in onCreateWindow callback but when i run it in release mode, i get an about blank.

also i know for a fact that the webapp which i open in webview does a window.post() to the new tab that is being opened.

We have stopped fixing this issue on flutter side as i couldnt find any feasible library or a way to fix this and instead make the new url make api call instead of using window.post() in the webapp, which is a reasonable workaround for us

pichillilorenzo commented 4 years ago

For example, at this moment, the Android onCreateWindow implementation can be found here: https://github.com/pichillilorenzo/flutter_inappwebview/blob/9fda80ae157ab61a813f9065a61bf851ebd9eba8/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewChromeClient.java#L476

If needed, I create a temp WebView to get the URL and then I destroy it. Instead, I need to change it in order to be able to create an InAppWebView instance from the Flutter side, otherwise, the created WebView will result in an always closed window. Also, this should be done on iOS.

I need to think about it and how to do that.