keymanapp / keyman

Keyman cross platform input methods system running on Android, iOS, Linux, macOS, Windows and mobile and desktop web
https://keyman.com/
Other
390 stars 108 forks source link

bug(web): `Uncaught TypeError: this.getTextBeforeCaret() is undefined` #10693

Closed mcdurdin closed 7 months ago

mcdurdin commented 7 months ago

There is still a consistent error when moving the focus from an attached content-editable element to elsewhere on the page (other than another attached element):

Uncaught TypeError: this.getTextBeforeCaret() is undefined
    getDeadkeyCaret contentEditable.ts:105
    buildTransformFrom outputTarget.ts:122
    buildTranscriptionFrom outputTarget.ts:200
    process kbdInterface.ts:1081
    processNewContextEvent kbdInterface.ts:1014
    processNewContextEvent keyboardProcessor.ts:239
    processNewContextEvent inputProcessor.ts:73
    resetContext inputProcessor.ts:358
    resetContext kmwbase.ts:509
    _ControlBlur domEventHandlers.ts:200
contentEditable.ts:105:13

Sample page:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="width=device-width, user-scalable=no" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" >
        <title>KeymanWeb Testing </title>
        <script src='https://s.keyman.com/kmw/engine/16.0.145/keymanweb.js'></script>
        <script src='https://s.keyman.com/kmw/engine/16.0.145/kmwuibutton.js'></script>
        <link rel='stylesheet' href='/kmw/kmwosk.css' type='text/css; charset=utf-8' />

        <script>
            init = function() {
                keyman.init({
                    attachType:'manual'
                    }).then(function() {
                        keyman.addKeyboards('@th','@lo','@en');         
                        var pt = document.getElementById('thai'),
                                pl = document.getElementById('lao');
                                ps = document.getElementById('cd');
                        if(pt) keyman.attachToControl(pt);
                        if(pl) keyman.attachToControl(pl);
                        if(ps) keyman.attachToControl(ps);
                        if(pt) keyman.setKeyboardForControl(pt,'Keyboard_basic_kbdth0');
                        if(pl) keyman.setKeyboardForControl(pl,'Keyboard_basic_kbdlao');
                        if(ps) keyman.setKeyboardForControl(ps,'Keyboard_basic_kbdth0');
                    });
            }           
            window.addEventListener('load',()=>{init();});
        </script>
        <style>
            .main{padding:4em;}
            input {margin:2em; width:10em}
            div#cd {display:block;position:relative; left:4em; top:0; height:4em;width:40em;border:1px solid blue;background-color:pink;}

        </style>
    </head>
    <body>
        <div class='main'>
        <p> Click on the content editable (pink) area then to a blank area of the window </p>
        <div contenteditable id='cd'></div>
            <label for 'thai'>Thai</label>
            <input id='thai' />
            <label for 'lao'>Lao</label>
            <input id='lao' />
        </div>
    </body>
</html>

Reported by @jmdurdin

jahorton commented 7 months ago

This wasn't very consistent in Chrome - I only got it to happen once. But... it was remarkably consistent in Firefox.

After setting things up decently...

image

Oh, wait... yeah, if we return nothing there, not even a ''... that'd do it.

For contrast, in 17.0:

https://github.com/keymanapp/keyman/blob/66142be3930c24f996939f7496a377f23223e3c3/web/src/engine/element-wrappers/src/contentEditable.ts#L109-L112

It's already fixed there, having landed in #8875 thanks to this review comment as part of the ES module work.

jahorton commented 7 months ago

This is basically a duplicate of #10620, so I'm closing it.