Skip to content
This repository has been archived by the owner on Mar 14, 2023. It is now read-only.

Bugs when passing through a string with \n or " on mobile - inconsistent behavior between platforms #76

Open
gabrc52 opened this issue Jun 28, 2022 · 2 comments · May be fixed by #82
Open

Comments

@gabrc52
Copy link

gabrc52 commented Jun 28, 2022

Sample code where bug is reproduced

This sample has a Dart text field on the top, and an HTML textarea on the bottom.

lib/main.dart
import 'package:flutter/material.dart';
import 'package:webviewx/webviewx.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late WebViewXController webviewController;
  late String json;
  final textController = TextEditingController();
  String str = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bug demo'),
      ),
      body: Column(
        children: [
          TextField(
            controller: textController,
            onChanged: (value) => str = value,
            maxLines: 5,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton.icon(
                icon: const Icon(Icons.download),
                onPressed: () async {
                  await webviewController.callJsMethod('load', [str]);
                },
                label: const Text('Dart to JS'),
              ),
              ElevatedButton.icon(
                icon: const Icon(Icons.upload),
                onPressed: () async {
                  str = await webviewController.callJsMethod('save', []);
                  textController.text = str;
                },
                label: const Text('JS to Dart'),
              ),
            ],
          ),
          Expanded(
            child: WebViewX(
              onWebViewCreated: (controller) async {
                webviewController = controller;
                await webviewController.loadContent(
                  '''<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <textarea rows="5" cols="40" id="str"></textarea>
    <script>
      function load(str) {
        document.getElementById('str').value = str;
      }
      function save() {
        return document.getElementById('str').value;
      }
    </script>
  </body>
</html>
''',
                  SourceType.html,
                );
              },
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
            ),
          ),
        ],
      ),
    );
  }
}

Summary

When passing to and returning from callJsMethod, the following inconsistency in behavior occurs:

  • On Android, when converting strings from JS to Dart, newlines, quotes and backslashes become escaped, and when doing the opposite the opposite happens (they become unescaped).

  • On iOS, JS to Dart strings are kept the same (as expected) but when converting from Dart to JS, instances of \n turn into actual newlines. Quotes remain the same.

  • On Web, strings are kept the same (as expected).

Steps to reproduce

Keep the following strings in mind:

String 1

the quick brown fox
"jumps over the\nlazy dog"

String 2

the quick brown fox "jumps over the\nlazy dog"

If on iOS, disable smart quotes or manually paste the strings.

  1. Enter string 1 on the Dart text field
  2. Click on "Dart to JS"
  3. Click on "JS to Dart"
  4. Enter string 1 on the HTML text field
  5. Click on "JS to Dart"
  6. Click on "Dart to JS"
  7. Repeat the same steps for string 2 if needed

Android behavior

  • When string 1 is converted from Dart to JS:

Unsuccessful, prints the following:

I/chromium( 5718): [INFO:CONSOLE(1)] "Uncaught SyntaxError: Invalid or unexpected token"
  • When string 1 is converted from Dart to JS back to Dart:

N/A

  • When string 1 is converted from JS to Dart:
the quick brown fox\n\"jumps over the\\nlazy dog\"

(i.e. newlines, quotes and backslashes become escaped)

  • When string 1 is converted from JS to Dart back to JS:
the quick brown fox
"jumps over the\nlazy dog"

(i.e. newlines, quotes and backslashes become unescaped, and there is no net change)


  • When string 2 is converted from Dart to JS:
the quick brown fox "jumps over the
lazy dog"
  • When string 2 is converted from Dart to JS back to Dart:
the quick brown fox \"jumps over the\nlazy dog\"

iOS behavior

  • When string 1 is converted from Dart to JS:

Unsuccessful, PlatformException is thrown:

[VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: PlatformException(evaluateJavaScript_failed, Failed evaluating JavaScript, JavaScript string was: 'load('the quick brown fox
"jumps over the\nlazy dog"')'
  • When string 1 is converted from Dart to JS back to Dart:

N/A

  • When string 1 is converted from JS to Dart:
the quick brown fox
"jumps over the\nlazy dog"

(as expected)

  • When string 1 is converted from JS to Dart back to JS.

(PlatformException is thrown)

  • When string 2 is converted from Dart to JS:
the quick brown fox "jumps over the
lazy dog"
  • When string 2 is converted from Dart to JS back to Dart:
the quick brown fox "jumps over the
lazy dog"
  • When string 2 is converted from JS to Dart:
the quick brown fox "jumps over the\nlazy dog"

(as expected)

  • When string 2 is converted from JS to Dart back to JS
the quick brown fox "jumps over the
lazy dog"

Web behavior

Everything works as expected
  • When string 1 is converted from Dart to JS:
the quick brown fox
"jumps over the\nlazy dog"

(as expected)

  • When string 1 is converted from Dart to JS back to Dart:
the quick brown fox
"jumps over the\nlazy dog"

(as expected)

  • When string 1 is converted from JS to Dart:
the quick brown fox
"jumps over the\nlazy dog"

(as expected)

  • When string 1 is converted from Dart to JS:
the quick brown fox
"jumps over the\nlazy dog"

(as expected)

Expected behavior

Strings are kept exactly the same when switching between programming languages, and do not mutate.

Background

I'm doing something similar but for JSON files. When passing information between languages, valid JSON files turn into invalid JSON files. I have to workaround this by doing the opposite with find and replace or something similar.

I was passing JSON in the first place, because maps didn't work as expected either.

@gabrc52
Copy link
Author

gabrc52 commented Jun 28, 2022

Workaround on Android?

Dart to JS (passing arguments):

if (UniversalPlatform.isAndroid) {
  jsonString = jsonString.replaceAll('\\n', '\\\\n');
}

JS to Dart (returning):

if (UniversalPlatform.isAndroid) {
  json = json.replaceAll('\\\\n', '\\n').replaceAll('\\"', '"');
}

@gabrc52
Copy link
Author

gabrc52 commented Jul 9, 2022

I just submitted a pull request that should fix this issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant