From 47deaf50a20bf996cc93c18ea61160d3f0b97fb9 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Fri, 5 Jan 2024 12:37:45 +0100 Subject: [PATCH] core: Fix non-ASCII characters input in EditText MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Casting the character to u8 and back to char caused some non-ASCII non-control characters to be treated as control characters. For instance the letter "ą" (U+0105) after casting to u8 and back became ENQ (U+0005) which is a control character. Some other letters worked, for instance the letter "ł" (U+0142) became "B" (U+0042) and was not classified as a control character. The test edittext_input was added to verify this behavior. --- core/src/display_object/edit_text.rs | 63 +++++----- .../tests/swfs/avm1/edittext_input/input.json | 116 ++++++++++++++++++ .../swfs/avm1/edittext_input/input.json.py | 18 +++ .../tests/swfs/avm1/edittext_input/input.txt | 1 + .../tests/swfs/avm1/edittext_input/output.txt | 1 + tests/tests/swfs/avm1/edittext_input/test.as | 9 ++ tests/tests/swfs/avm1/edittext_input/test.swf | Bin 0 -> 216 bytes .../tests/swfs/avm1/edittext_input/test.toml | 1 + 8 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 tests/tests/swfs/avm1/edittext_input/input.json create mode 100755 tests/tests/swfs/avm1/edittext_input/input.json.py create mode 100644 tests/tests/swfs/avm1/edittext_input/input.txt create mode 100644 tests/tests/swfs/avm1/edittext_input/output.txt create mode 100644 tests/tests/swfs/avm1/edittext_input/test.as create mode 100644 tests/tests/swfs/avm1/edittext_input/test.swf create mode 100644 tests/tests/swfs/avm1/edittext_input/test.toml diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 50b19378c..ca022c9b8 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -1475,43 +1475,38 @@ impl<'gc> EditText<'gc> { if let Some(selection) = self.selection() { let mut changed = false; let mut cancelled = false; - match character as u8 { - code if !(code as char).is_control() => { - if self.available_chars() > 0 { - if let Avm2Value::Object(target) = self.object2() { - let character_string = - AvmString::new_utf8(context.gc_context, character.to_string()); + if !character.is_control() && self.available_chars() > 0 { + if let Avm2Value::Object(target) = self.object2() { + let character_string = + AvmString::new_utf8(context.gc_context, character.to_string()); - let mut activation = Avm2Activation::from_nothing(context.reborrow()); - let text_evt = Avm2EventObject::text_event( - &mut activation, - "textInput", - character_string, - true, - true, - ); - Avm2::dispatch_event(&mut activation.context, text_evt, target); + let mut activation = Avm2Activation::from_nothing(context.reborrow()); + let text_evt = Avm2EventObject::text_event( + &mut activation, + "textInput", + character_string, + true, + true, + ); + Avm2::dispatch_event(&mut activation.context, text_evt, target); - cancelled = text_evt.as_event().unwrap().is_cancelled(); - } - - if !cancelled { - self.replace_text( - selection.start(), - selection.end(), - &WString::from_char(character), - context, - ); - let new_pos = selection.start() + character.len_utf8(); - self.set_selection( - Some(TextSelection::for_position(new_pos)), - context.gc_context, - ); - changed = true; - } - } + cancelled = text_evt.as_event().unwrap().is_cancelled(); + } + + if !cancelled { + self.replace_text( + selection.start(), + selection.end(), + &WString::from_char(character), + context, + ); + let new_pos = selection.start() + character.len_utf8(); + self.set_selection( + Some(TextSelection::for_position(new_pos)), + context.gc_context, + ); + changed = true; } - _ => {} } if changed { diff --git a/tests/tests/swfs/avm1/edittext_input/input.json b/tests/tests/swfs/avm1/edittext_input/input.json new file mode 100644 index 000000000..08eee03f2 --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/input.json @@ -0,0 +1,116 @@ +[ + { "type": "TextInput", "codepoint": "a" }, + { "type": "TextInput", "codepoint": "b" }, + { "type": "TextInput", "codepoint": "c" }, + { "type": "TextInput", "codepoint": "A" }, + { "type": "TextInput", "codepoint": "B" }, + { "type": "TextInput", "codepoint": "C" }, + { "type": "TextInput", "codepoint": "~" }, + { "type": "TextInput", "codepoint": "!" }, + { "type": "TextInput", "codepoint": "@" }, + { "type": "TextInput", "codepoint": "#" }, + { "type": "TextInput", "codepoint": "$" }, + { "type": "TextInput", "codepoint": "%" }, + { "type": "TextInput", "codepoint": "^" }, + { "type": "TextInput", "codepoint": "&" }, + { "type": "TextInput", "codepoint": "*" }, + { "type": "TextInput", "codepoint": "(" }, + { "type": "TextInput", "codepoint": ")" }, + { "type": "TextInput", "codepoint": "`" }, + { "type": "TextInput", "codepoint": "-" }, + { "type": "TextInput", "codepoint": "=" }, + { "type": "TextInput", "codepoint": "_" }, + { "type": "TextInput", "codepoint": "+" }, + { "type": "TextInput", "codepoint": "/" }, + { "type": "TextInput", "codepoint": "?" }, + { "type": "TextInput", "codepoint": "|" }, + { "type": "TextInput", "codepoint": "ą" }, + { "type": "TextInput", "codepoint": "ę" }, + { "type": "TextInput", "codepoint": "ć" }, + { "type": "TextInput", "codepoint": "ł" }, + { "type": "TextInput", "codepoint": "þ" }, + { "type": "TextInput", "codepoint": "ó" }, + { "type": "TextInput", "codepoint": "→" }, + { "type": "TextInput", "codepoint": "↓" }, + { "type": "TextInput", "codepoint": "ß" }, + { "type": "TextInput", "codepoint": "©" }, + { "type": "TextInput", "codepoint": "ę" }, + { "type": "TextInput", "codepoint": "œ" }, + { "type": "TextInput", "codepoint": "π" }, + { "type": "TextInput", "codepoint": "ą" }, + { "type": "TextInput", "codepoint": "ś" }, + { "type": "TextInput", "codepoint": "ð" }, + { "type": "TextInput", "codepoint": "æ" }, + { "type": "TextInput", "codepoint": "ŋ" }, + { "type": "TextInput", "codepoint": "’" }, + { "type": "TextInput", "codepoint": "ə" }, + { "type": "TextInput", "codepoint": "…" }, + { "type": "TextInput", "codepoint": "ł" }, + { "type": "TextInput", "codepoint": "ź" }, + { "type": "TextInput", "codepoint": "ć" }, + { "type": "TextInput", "codepoint": "ż" }, + { "type": "TextInput", "codepoint": "„" }, + { "type": "TextInput", "codepoint": "”" }, + { "type": "TextInput", "codepoint": "ń" }, + { "type": "TextInput", "codepoint": "µ" }, + { "type": "TextInput", "codepoint": "≤" }, + { "type": "TextInput", "codepoint": "≥" }, + { "type": "TextInput", "codepoint": "≠" }, + { "type": "TextInput", "codepoint": "²" }, + { "type": "TextInput", "codepoint": "³" }, + { "type": "TextInput", "codepoint": "¢" }, + { "type": "TextInput", "codepoint": "€" }, + { "type": "TextInput", "codepoint": "½" }, + { "type": "TextInput", "codepoint": "§" }, + { "type": "TextInput", "codepoint": "·" }, + { "type": "TextInput", "codepoint": "«" }, + { "type": "TextInput", "codepoint": "»" }, + { "type": "TextInput", "codepoint": "–" }, + { "type": "TextInput", "codepoint": "—" }, + { "type": "TextInput", "codepoint": "°" }, + { "type": "TextInput", "codepoint": "±" }, + { "type": "TextInput", "codepoint": "¾" }, + { "type": "TextInput", "codepoint": "≈" }, + { "type": "TextInput", "codepoint": "∧" }, + { "type": "TextInput", "codepoint": "‰" }, + { "type": "TextInput", "codepoint": "¼" }, + { "type": "TextInput", "codepoint": "£" }, + { "type": "TextInput", "codepoint": "¿" }, + { "type": "TextInput", "codepoint": "¡" }, + { "type": "TextInput", "codepoint": "Ƈ" }, + { "type": "TextInput", "codepoint": "ƌ" }, + { "type": "TextInput", "codepoint": "ƣ" }, + { "type": "TextInput", "codepoint": "Ʊ" }, + { "type": "TextInput", "codepoint": "Lj" }, + { "type": "TextInput", "codepoint": "Ǎ" }, + { "type": "TextInput", "codepoint": "Ǜ" }, + { "type": "TextInput", "codepoint": "ǭ" }, + { "type": "TextInput", "codepoint": "Ǽ" }, + { "type": "TextInput", "codepoint": "ȉ" }, + { "type": "TextInput", "codepoint": "ȥ" }, + { "type": "TextInput", "codepoint": "ȹ" }, + { "type": "TextInput", "codepoint": "Ɍ" }, + { "type": "TextInput", "codepoint": "ɏ" }, + { "type": "TextInput", "codepoint": "Ḑ" }, + { "type": "TextInput", "codepoint": "ṁ" }, + { "type": "TextInput", "codepoint": "Ṥ" }, + { "type": "TextInput", "codepoint": "Ẉ" }, + { "type": "TextInput", "codepoint": "ẟ" }, + { "type": "TextInput", "codepoint": "Ắ" }, + { "type": "TextInput", "codepoint": "ố" }, + { "type": "TextInput", "codepoint": "ỳ" }, + { "type": "TextInput", "codepoint": "ỿ" }, + { "type": "TextInput", "codepoint": "Ⱨ" }, + { "type": "TextInput", "codepoint": "Ȿ" }, + { "type": "TextInput", "codepoint": "Ꞧ" }, + { "type": "TextInput", "codepoint": "Ꞁ" }, + { "type": "TextInput", "codepoint": "Ꞿ" }, + { "type": "TextInput", "codepoint": "ꟿ" }, + { "type": "TextInput", "codepoint": "ꭧ" }, + { "type": "TextInput", "codepoint": "𐞔" }, + { "type": "TextInput", "codepoint": "𐞮" }, + { "type": "TextInput", "codepoint": "𝼙" }, + { "type": "TextInput", "codepoint": "𝼞" }, + { "type": "TextInput", "codepoint": "စ" }, + { "type": "KeyDown", "key_code": 27 } +] diff --git a/tests/tests/swfs/avm1/edittext_input/input.json.py b/tests/tests/swfs/avm1/edittext_input/input.json.py new file mode 100755 index 000000000..969328758 --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/input.json.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import sys +from os import path + +sys.stdout.reconfigure(encoding='utf-8') + +input_file = path.join(path.dirname(__file__), 'input.txt') +with open(input_file, 'r', encoding='utf-8') as file: + characters = file.read().replace(' ', '').replace('\n', '') + +print('[') +for ch in characters: + print(f' {{ "type": "TextInput", "codepoint": "{ch}" }},') + +print(f' {{ "type": "KeyDown", "key_code": 27 }}') + +print(']') diff --git a/tests/tests/swfs/avm1/edittext_input/input.txt b/tests/tests/swfs/avm1/edittext_input/input.txt new file mode 100644 index 000000000..391387bbf --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/input.txt @@ -0,0 +1 @@ +abcABC~!@#$%^&*()`-=_+/?|ąęćłþó→↓ß©ęœπąśðæŋ’ə…łźćż„”ńµ≤≥≠²³¢€½§·«»–—°±¾≈∧‰¼£¿¡ƇƌƣƱLjǍǛǭǼȉȥȹɌɏḐṁṤẈẟẮốỳỿⱧⱾꞦꞀꞾꟿꭧ𐞔𐞮𝼙𝼞စ diff --git a/tests/tests/swfs/avm1/edittext_input/output.txt b/tests/tests/swfs/avm1/edittext_input/output.txt new file mode 100644 index 000000000..391387bbf --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/output.txt @@ -0,0 +1 @@ +abcABC~!@#$%^&*()`-=_+/?|ąęćłþó→↓ß©ęœπąśðæŋ’ə…łźćż„”ńµ≤≥≠²³¢€½§·«»–—°±¾≈∧‰¼£¿¡ƇƌƣƱLjǍǛǭǼȉȥȹɌɏḐṁṤẈẟẮốỳỿⱧⱾꞦꞀꞾꟿꭧ𐞔𐞮𝼙𝼞စ diff --git a/tests/tests/swfs/avm1/edittext_input/test.as b/tests/tests/swfs/avm1/edittext_input/test.as new file mode 100644 index 000000000..17a1899c2 --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/test.as @@ -0,0 +1,9 @@ +var listener = new Object(); +listener.onKeyDown = function() { + if (Key.getCode() == 27) { + trace(text.text); + } +}; +Key.addListener(listener); + +Selection.setFocus(text); diff --git a/tests/tests/swfs/avm1/edittext_input/test.swf b/tests/tests/swfs/avm1/edittext_input/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..c135b87eee9373eac2f6cb6a28c95c09250d532f GIT binary patch literal 216 zcmV;}04M)LS5pTl0RRAaoU349zresCUckU0!N}ku$N&V+O#lD?58`BGs9<1u1myo> zVBr9XGjy>jF)}bRl%!UaFxWHS1B!I`FmN*DWEPjC=A{-f_$OthCYLbe=Xs}Ay5yJV zF#u_X^wbjP{FGFPuEdlSADHUk)EuDd%=|ot;?xqi{N&PNhG|R;91PP~8Q4L#fjEo~ zHefLs5Xm%~6(nweqJkML#S#P(lLm@;3e9C=-~~&uf~8pf)cm1l0F^MJXk!Z!hblyp SVh2lc1c@^^07U@so;a97#$ufS literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm1/edittext_input/test.toml b/tests/tests/swfs/avm1/edittext_input/test.toml new file mode 100644 index 000000000..dbee897f5 --- /dev/null +++ b/tests/tests/swfs/avm1/edittext_input/test.toml @@ -0,0 +1 @@ +num_frames = 1