Repository: flutter
Issue: [webview_flutter] cookie is not loaded with `WebViewController.loadRequest` if server sets `HttpOnly; Secure` on headers
Labels: p: webview,package,team-ecosystem,has reproducible steps,P2,triaged-ecosystem,found in release: 3.13,found in release: 3.17

### Is there an existing issue for this?

- [X] I have searched the [existing issues](https://github.com/flutter/flutter/issues)
- [X] I have read the [guide to filing a bug](https://flutter.dev/docs/resources/bug-reports)

### Steps to reproduce

1. Create new project and copy and paste `main.dart`
2. Have a folder with `server.py` and `cookie.html` in it. Run `python server.py`
3. Run the app

### Expected results

* `username` should be set to `DuckDuckGo` as defined in by `server.py`

### Actual results

No cookie being set via `Set-Cookie` in the header.

~If you remove the `Secure` keyword in `server.py` `Set-Cookie`, it works.~

~Different behaviour on iOS and Android. Android and iOS doesn't seem to accept the cookie. When `Secure` is not part of the cookie params, iOS accepts the cookie but just ignores it.~

### Code sample


<details><summary>main.dart</summary>

```dart

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

const cookieKey = "username";
const domain = "127.0.0.1";
const htmlHomeUrl = "http://$domain:8000/cookie.html";
const setCookieHomeUrl = "http://$domain:8000/set-cookie";
const bool shouldInitCookieViaSetCookieUrl = true;

String homeUrl() => htmlHomeUrl;

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late WebViewController _webViewController;
  String? _cookieValue;

  @override
  void initState() {
    _webViewController = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted);
    _loadCookieFromServer();

    super.initState();
  }

  Future<void> _loadCookieFromServer() async {
    print("load cookie from server");

    await _webViewController.loadRequest(
      Uri.parse(setCookieHomeUrl),
      method: LoadRequestMethod.get,
    );

    final String cookieString = await _webViewController
        .runJavaScriptReturningResult('document.cookie').then((value) {
          final v = value as String;
          return v.substring(1, v.length - 1);
        });

    print("cookies: `$cookieString`");

    if (cookieString.isNotEmpty && cookieString != "\"\"" && cookieString != "null") {
      final cookies = cookieString.split(";");
      for(final cookie in cookies) {
        final [key, value] = cookie.split("=");
        print(" - $key:$value");
        if (key == cookieKey) {
          _cookieValue = value;
        }
      }
    }
  } 

  Future<void> _loadCookie() async {
    // webview_cookie_manager cookie manager
    print("load cookie");

    final String cookieString = await _webViewController
      .runJavaScriptReturningResult('document.cookie').then((value) {
        final v = value as String;
        return v.substring(1, v.length - 1);
      });

    print("cookies: `$cookieString` ${cookieString.isNotEmpty}");

    if (cookieString.isNotEmpty && cookieString != "\"\"" && cookieString != "null") {
      final cookies = cookieString.split(";");
      for(final cookie in cookies) {
        final [key, value] = cookie.split("=");
        print(" - $key:$value");
        if (key == cookieKey) {
          _cookieValue = value;
        }
      }
    }

    _cookieValue = null;

    setState(() {
      
    });
  }

  void _setCookie() async {
    await _loadCookieFromServer();
  }

  void _onSetCookiePressed() {
    _setCookie();
  }

  void _onClearCookiesPressed() {
    // webview_flutter cookie manager
    WebViewCookieManager().clearCookies();

    _loadCookie();
  }

  void _onOpenWebPagePressed() async {
    Navigator.of(context).push(MaterialPageRoute(builder: (_) => 
      WebViewPage.url(Uri.parse(homeUrl()))
    ));
  }

  void _onReloadPressed() {
    print("reload");
    _loadCookie();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final value = (_cookieValue != null)
      ? _cookieValue!
      : "NULL";

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Spacer(),
            Text("Cookie `$cookieKey`", style: theme.textTheme.bodyLarge,),
            Text(value),
            const SizedBox(height: 8.0,),
            ElevatedButton(
              onPressed: _onSetCookiePressed,
              child: const Text("Set Cookie Header"),
            ),
            TextButton(
              onPressed: _onClearCookiesPressed,
              child: const Text("Clear Cookies")
            ),
            
            TextButton(
              onPressed: _onOpenWebPagePressed,
              child: const Text("Open Webpage"),
            ),

            TextButton(
              onPressed: _onReloadPressed,
              child: const Text("Reload")
            ),
            const Spacer(),
            Container(
              width: 100,
              height: 100,
              child: WebViewWidget(
                controller: _webViewController,
              ),
            ),
            const SizedBox(height: 8.0,),
          ],
        ),
      ),
    );
  }
}

/* Web View Page */

sealed class _WebType {
  const _WebType();
}

class _UrlWebType extends _WebType {
  final Uri url;

  const _UrlWebType(this.url);
}

class _HtmlWebType extends _WebType {
  final String html;

  const _HtmlWebType(this.html);
}

typedef OnMessageReceived = void Function(String msg);

class WebViewPage extends StatefulWidget {
  final _WebType _webType;
  final Widget? title;
  final OnMessageReceived? onMessageReceived;

  const WebViewPage._({
    super.key,
    required _WebType webType,
    this.title,
    this.onMessageReceived,
  }) : _webType = webType;

  WebViewPage.url(Uri url, {
    Key? key,
    Widget? title,
    OnMessageReceived? onMessageReceived,
  }) : this._(
    key: key,
    webType: _UrlWebType(url),
    title: title,
    onMessageReceived: onMessageReceived
  );

  WebViewPage.html(String html, {
    Key? key,
    Widget? title,
    OnMessageReceived? onMessageReceived,
  }) : this._(
    key: key,
    webType: _HtmlWebType(html),
    title: title,
    onMessageReceived: onMessageReceived
  );


  @override
  State<StatefulWidget> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  late WebViewController _controller;
  late double _progress;
  late bool _showProgress;

  void _checkCookies() async {
    final String cookieString = await _controller
      .runJavaScriptReturningResult('document.cookie').then((value) {
        final v = value as String;
        return v.replaceAll("\"", "");
      });

    print("check cookies: $cookieString");

    if (cookieString.isNotEmpty && cookieString != "\"\"" && cookieString != "null") {
      final cookies = cookieString.split(";");
      for(final cookie in cookies) {
        final [key, value] = cookie.split("=");
        print(" - $key:$value");
      }
    }
  }

  @override
  void initState() {
    _progress = 0;
    _showProgress = true;

    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(Colors.white)
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {
            setState(() {
              _progress = progress / 100;
            });
          },
          onPageStarted: (String url) {
            setState(() {
              _showProgress = true;
              _progress = 0;
            });
          },
          onPageFinished: (String url) {
            _checkCookies();
            setState(() {
              _showProgress = false;
            });
          },
          onWebResourceError: (WebResourceError error) {},
          onNavigationRequest: (NavigationRequest request) {
            // if (request.url.startsWith('https://www.youtube.com/')) {
            //   return NavigationDecision.prevent;
            // }
            return NavigationDecision.navigate;
          },
        ),
      );
    
    switch (widget._webType) {
      case _UrlWebType(url: final url):
        _controller.loadRequest(url);
        break;
      case _HtmlWebType(html: final html):
        _controller.loadHtmlString(html);
        break;
    }

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(
        title: widget.title,
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(6.0),
          child: Opacity(
            opacity: _showProgress ? 1.0 : 0.0,
            child: LinearProgressIndicator(
              backgroundColor: theme.colorScheme.onPrimary,
              valueColor: AlwaysStoppedAnimation<Color>(theme.colorScheme.primary),
              value: _progress,
            ),
          )
        ),
      ),
      body: WebViewWidget(controller: _controller,),
    );
  }
}
```

</details>

<details><summary>cookie.html</summary>

```html
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 300px;
            margin: 0 auto;
            padding: 10px;
        }
        label, input {
            display: block;
            margin-bottom: 10px;
        }
        button {
            width: 100%;
            padding: 10px;
            background-color: #007BFF;
            color: #fff;
            border: none;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        #cookieResult {
            margin-top: 10px;
        }
    </style>
    <title>Cookie Example</title>
</head>
<body>
    <h1>Cookie Example</h1>

    <p>This page can read and set a cookie.</p>

    <label for="cookieValue">Cookie Value:</label>
    <input type="text" id="cookieValue">

    <button onclick="setCookie()">Set Cookie</button>
    <button onclick="getCookieValue()">Get Cookie Value</button>
    <button onclick="deleteCookie()">Delete Cookie</button>

    <p id="cookieResult"></p>

    <script>
        // Function to set a cookie
        function setCookie() {
            var name = "username"; // Hardcoded cookie name
            var value = document.getElementById("cookieValue").value;

            if (value) {
                var days = 1; // Hardcoded expiration to 1 day
                var expires = new Date();
                expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
                document.cookie = name + "=" + value + "; expires=" + expires.toUTCString() + "; path=/";

                document.getElementById("cookieResult").textContent = "Cookie '" + name + "' has been set to expire in 1 day.";
            } else {
                document.getElementById("cookieResult").textContent = "Please enter a value for the cookie.";
            }
        }

        // Function to read a cookie
        function getCookieValue() {
            var name = "username"; // Hardcoded cookie name
            var cookieValue = getCookie(name);

            if (cookieValue) {
                document.getElementById("cookieResult").textContent = "The value of the '" + name + "' cookie is: " + cookieValue;
            } else {
                document.getElementById("cookieResult").textContent = "The '" + name + "' cookie is not set.";
            }
        }

        // Function to delete a cookie
        function deleteCookie() {
            var name = "username"; // Hardcoded cookie name
            document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"; // Expire the cookie

            document.getElementById("cookieResult").textContent = "Cookie '" + name + "' has been deleted.";
        }

        // Function to read a cookie
        function getCookie(name) {
            var nameEQ = name + "=";
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = cookies[i];
                while (cookie.charAt(0) == ' ') {
                    cookie = cookie.substring(1, cookie.length);
                }
                if (cookie.indexOf(nameEQ) == 0) {
                    return cookie.substring(nameEQ.length, cookie.length);
                }
            }
            return null;
        }
    </script>
</body>
</html>
```

</details>

<details><summary>server.py</summary>

```python
import http.server
import socketserver

# Create a custom handler that sets a cookie
class CustomHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/set-cookie':
            # Set a cookie in the response header
            self.send_response(200)
            self.send_header('Set-Cookie', 'username=DuckDuckGo; Path=/; Expires=Thu, 02 Nov 2023 11:10:56 GMT; Max-Age=86400; HttpOnly; Secure')
            self.end_headers()
            self.wfile.write(b'Cookie set!')
        else:
            # Serve files for other requests
            super().do_GET()

# Create a simple HTTP server with the custom handler
port = 8000
with socketserver.TCPServer(("", port), CustomHandler) as httpd:
    print(f"Serving on port {port}")
    httpd.serve_forever()

```

</details>

### Screenshots or Video

<details>
<summary>Screenshots / Video demonstration</summary>

[Upload media here]

</details>


### Logs

<details><summary>Logs</summary>

```console
Launching lib/main.dart on Pixel 4a in debug mode...
main.dart:1
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:53410/hPNQT0fI86Q=/ws
2
W/ziparchive(15062): Unable to open '/data/app/~~_6MBV44gqLRoi89A6JuAMA==/com.google.android.trichromelibrary_599311131-OWPoiXyx_eAMohzAl2JFjg==/base.dm': No such file or directory
W/ple.test_cookie(15062): Entry not found
D/nativeloader(15062): Configuring clns-5 for other apk /data/app/~~_6MBV44gqLRoi89A6JuAMA==/com.google.android.trichromelibrary_599311131-OWPoiXyx_eAMohzAl2JFjg==/base.apk. target_sdk_version=34, uses_libraries=ALL, library_path=/data/app/~~cik95RSPsQ1ThdH2-iHoAA==/com.google.android.webview-JJNzp-p3OFWc_rWRfSMopg==/lib/arm64:/data/app/~~cik95RSPsQ1ThdH2-iHoAA==/com.google.android.webview-JJNzp-p3OFWc_rWRfSMopg==/base.apk!/lib/arm64-v8a:/data/app/~~_6MBV44gqLRoi89A6JuAMA==/com.google.android.trichromelibrary_599311131-OWPoiXyx_eAMohzAl2JFjg==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand
D/nativeloader(15062): Configuring clns-6 for other apk /data/app/~~cik95RSPsQ1ThdH2-iHoAA==/com.google.android.webview-JJNzp-p3OFWc_rWRfSMopg==/base.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~cik95RSPsQ1ThdH2-iHoAA==/com.google.android.webview-JJNzp-p3OFWc_rWRfSMopg==/lib/arm64:/data/app/~~cik95RSPsQ1ThdH2-iHoAA==/com.google.android.webview-JJNzp-p3OFWc_rWRfSMopg==/base.apk!/lib/arm64-v8a:/data/app/~~_6MBV44gqLRoi89A6JuAMA==/com.google.android.trichromelibrary_599311131-OWPoiXyx_eAMohzAl2JFjg==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand
I/WebViewFactory(15062): Loading com.google.android.webview version 118.0.5993.111 (code 599311131)
I/cr_WVCFactoryProvider(15062): Loaded version=118.0.5993.111 minSdkVersion=29 isBundle=true multiprocess=true packageId=2
E/chromium(15062): [1101/114639.654205:ERROR:variations_seed_loader.cc(69)] Failed to open file for reading. Errno: 2
```

</details>


### Flutter Doctor output

<details><summary>Doctor output</summary>

```console
[✓] Flutter (Channel stable, 3.13.9, on macOS 12.6.2 21G320 darwin-arm64, locale
    en-GB)
    • Flutter version 3.13.9 on channel stable at /usr/local/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision d211f42860 (13 days ago), 2023-10-25 13:42:25 -0700
    • Engine revision 0545f8705d
    • Dart version 3.1.5
    • DevTools version 2.25.0

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
    • Android SDK at /Users/robbiebykowski/Library/Android/sdk
    • Platform android-33, build-tools 31.0.0
    • ANDROID_SDK_ROOT = /Users/robbiebykowski/Library/Android
    • Java binary at: /Users/robbiebykowski/Library/Application
      Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/221.6008.13.2211.9514443
      /Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      11.0.15+0-b2043.56-8887301)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14C18
    • CocoaPods version 1.11.3

[✓] VS Code (version 1.76.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.74.0

[✓] Connected device (3 available)
    • Pixel 4a (mobile)                   • 11201JEC203710
      • android-arm64 • Android 13 (API 33)
    • iPhone SE (3rd generation) (mobile) • AC11B72F-52C6-4B5F-93D7-9CF12246A42A
      • ios           • com.apple.CoreSimulator.SimRuntime.iOS-16-2 (simulator)
    • macOS (desktop)                     • macos
      • darwin-arm64  • macOS 12.6.2 21G320 darwin-arm64

[✓] Network resources
    • All expected network resources are available.
```

</details>