Repository: flutter
Issue: [webview_flutter] runJavascript in onPageStarted not working in TestFlight
Labels: platform-ios,p: webview,package,a: production,P2,team-ios,triaged-ios

I have two deployed apps using this feature that has subsequently stopped working. If I now attempt any new uploads to TestFlight it no longer works. 

Currently, the code below works on a physical iPhone running through Android Studio for both debug and deploy, but when uploaded to TestFlight stops working.

The need is to inject JS into a webpage before the JS on the webpage loads. In the code example, both the output on the web page and the postMessage in a JS channel will show that either the web page JS ran or the injected JS ran.

## Steps to Reproduce

I am using “webview_flutter: ^3.0.1” in my pubspec.yaml. 

1. to see the normal functioning of the web page JS, comment out _controller.runJavascript(jsOverLoad); in the onPageStarted handler in the app code below and run. 

You should get 3 AlertDialogs. The top two alerts are triggered from `_controller.runJavascriptReturningResult('testing();')` in the `onPageFinished` handle and will show for the top one “RESULT: Return from WEB”. The next is coming from the channel and shows “CHANNEL: Hello from WEB Channel” The third Alert is triggered by JS on the web page which also comes through the channel and should show the same as the second Alert. Then on the web page displayed, you will see the last line reads “Return from Web”.

2. uncomment `_controller.runJavascript(jsOverLoad);` in the `onPageStarted` handler in the app code below and run. 

Now you will see (if you are running to a physical iPhone) the 3 Alerts with these messages: “RESULT: Injected Return” and “CHANNEL: Hello from My Injected Channel” And the last line on the displayed web page will read “Injected Return”.

3. deploy the app to TestFlight and you will get the results from the web page JS and not from the Injected JS, but the code `_controller.runJavascriptReturningResult('testing();')` in the `onPageFinished` handle still runs as expected.

<details>
<summary>Code sample</summary>

<!--
      Please create a minimal reproducible sample that shows the problem
      and attach it below between the lines with the backticks.

      To create it you can use `flutter create bug` command and update the `main.dart` file.

      Alternatively, you can use https://dartpad.dev/
      which is capable of creating and running small Flutter apps.

      Without this we will unlikely be able to progress on the issue, and because of that
      we regretfully will have to close it.
-->

```dart
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  final String jsOverLoad = '''
  (function() {
    
    window.testing = function(){
      try {
        MyChannel.postMessage("Hello from My Injected Channel");
      } catch (e) {}
      return "Injected Return";
    }
    
    window.testing();
    
  })();
  ''';

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  void _showMessage(String message) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Insight'),
        content: Text(message),
        actions: <Widget>[
          TextButton(
            child: const Text('OK'),
            onPressed: () {
              Navigator.pop(context);
            },),
        ],
      ),
    );
  }

  JavascriptChannel _myJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
      name: 'MyChannel',
      onMessageReceived: (JavascriptMessage message) {
        _showMessage('CHANNEL: ${message.message}');
      },);
  }

  @override
  Widget build(BuildContext context) {
    final _key = UniqueKey();
    late WebViewController _controller;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Injection Demo'),
      ),
      body: Column(
        children: [
          Expanded(
            child: WebView(
              key: _key,
              javascriptMode: JavascriptMode.unrestricted,
              javascriptChannels: <JavascriptChannel>{
                _myJavascriptChannel(context),
              },
              onWebViewCreated: (WebViewController webViewController) {
                _controller = webViewController;
                // NOTE: headers are not needed in this example but are included for testing because the actual app loads the web page this way.
                final Map<String, String> headers = {
                  'Authorization': 'Bearer test123'
                };
                _controller.loadUrl('https://js-injection-demo-c5dyn.ondigitalocean.app', headers: headers);
              },
              onPageStarted: (String url) {
                _controller.runJavascript(jsOverLoad);
              },
              onPageFinished: (String url) {
                _controller.runJavascriptReturningResult('testing();')
                    .then((result) {
                    _showMessage('RESULT: $result');
                  });
              },
            ),
          ),
        ],
      ),
    );
  }
}
```

</details>


<details>
<summary>HTML Code sample</summary>

```html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>JS Injection Demo Website</title>
  <!-- <script type="text/javascript" src="my-javascript.js"></script> -->
  <script>
    if (!window.testing) {
     window.testing = function(){
          try {
               MyChannel.postMessage("Hello from WEB Channel");
          } catch (e) { 
               console.warn('MyChannel is not defined');
          }
          return "Return from WEB";
       }
    }
  </script>
</head>

<body>

    <h1>JS Injection Demo Website</h1>
    <p><em>Testing JS injection issue.</em></p>
    <div id="output"></div>
    <script>
     var output = document.getElementById('output');
     output.innerHTML = window.testing();
    </script>
    
</body>

</html>
```

</details>

<details>
  <summary>Logs</summary>

<!--
      Run your application with `flutter run --verbose` and attach all the
      log output below between the lines with the backticks. If there is an
      exception, please see if the error message includes enough information
      to explain how to solve the issue.
-->


<!--
     Run `flutter analyze` and attach any output of that command below.
     If there are any analysis errors, try resolving them before filing this issue.
-->


<!-- Finally, paste the output of running `flutter doctor -v` here. -->

```
Flutter Doctor:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.10.4, on macOS 12.2.1 21D62 darwin-arm, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.1)
[✓] VS Code (version 1.66.0)
[✓] Connected device (2 available)
[✓] HTTP Host Availability

• No issues found!
```

</details>


<!--
      Consider also attaching screenshots and/or videos to better
      illustrate the issue.

      You can upload them directly on GitHub.
      Beware that video file size is limited to 10MB.
-->