Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug(web): The base context for the multitap could not be found #13344

Open
sentry-io bot opened this issue Feb 25, 2025 · 3 comments
Open

bug(web): The base context for the multitap could not be found #13344

sentry-io bot opened this issue Feb 25, 2025 · 3 comments
Assignees
Milestone

Comments

@sentry-io
Copy link

sentry-io bot commented Feb 25, 2025

Sentry Issue: KEYMAN-WEB-MY

Error:  The base context for the multitap could not be found
  at reduceConsoleArgs (/data/user/0/com.tavultesoft.kmapro/app_data/keyman-sentry.js:150:22)
  at console.reportingConsoleWarn [as warn] (/data/user/0/com.tavultesoft.kmapro/app_data/keyman-sentry.js:143:33)
  at _InputProcessor.processKeyEvent (@keymanapp/keyman/common/web/input-processor/src/text/inputProcessor.ts:125:19)
  at this.keyEventListener (@keymanapp/keyman/web/src/engine/main/src/keymanEngine.ts:83:30)
  at bc.<anonymous> (@keymanapp/keyman/web/src/engine/osk/src/views/oskView.ts:840:54)
...
(14 additional frame(s) were not displayed)
@jahorton
Copy link
Contributor

I've been trying to investigate this one today, though admittedly without much success.

Noted facts + patterns:

  • It appears that the majority of occurrences happen with sil_euro_latin. I've seen a few with UI-interaction breadcrumbs indicating that this can happen with the shift-key multitap.
  • The multitap tracks a unique id into a 'cache' of recent context-states. This cache's "buffer size" is currently set to hold the 10 most recent entries.
    • Multitap keystroke processing always and unfailingly looks up the base context and refreshes it in the context-cache.
    • Somehow, ten other context states occurred since the last time that the multitap's base context was last accessed.
    • If that ID were somehow null or undefined, the lookup operation would not occur. It is "truthy" for these cases.

// Support for multitap context reversion; multitap keys should act as if they were
// the first thing typed since `preInput`, the state before the original base key.
if(keyEvent.baseTranscriptionToken) {
const transcription = this.contextCache.get(keyEvent.baseTranscriptionToken);
if(transcription) {
// Has there been a context change at any point during the multitap? If so, we need
// to revert it. If not, we assume it's a layer-change multitap, in which case
// no such reset is needed.
if(!isEmptyTransform(transcription.transform) || !transcription.preInput.isEqual(Mock.from(outputTarget))) {
// Restores full context, including deadkeys in their exact pre-keystroke state.
outputTarget.restoreTo(transcription.preInput);
}
/*
else:
1. We don't need to restore the original context, as it's already
in-place.
2. Restoring anyway would obliterate any selected text, which is bad
if this is a purely-layer-switching multitap. (#11230)
*/
} else {
console.warn('The base context for the multitap could not be found');
}
}

The error message seen in the event requires a "truthy" baseTranscriptionToken, which is the context-state lookup ID referred to previously in this comment.

@jahorton
Copy link
Contributor

I've tested multitapping the sil_euro_latin shift key locally and can confirm that for standard use cases, that cache is properly renewing the base context-state, preserving it. It's not rotating out.

That said... a thought occurs to me. Back in 17.0's late-alpha / beta cycle, we had a series of issues arise with rapid typing on lower-powered Android devices. Essentially, keystrokes might end up being processed significantly after the actual touch-interaction occurred. My belief was that the final solution for that problem - #10843 - essentially acted as Web's version of a keystroke serializer like what we've had to implement for our desktop platforms (internal doc).

Alternatively, maybe I need to inspect the gesture-specs more closely to make 100% sure there's no way for something to slip through. While that Shift multitap is likely one of the most common cases possible, it's also significant that Shift can be modipressed - the associated gesture models are more complicated as a result.

@jahorton jahorton self-assigned this Feb 25, 2025
@jahorton jahorton added this to the B18S2 milestone Feb 25, 2025
@jahorton
Copy link
Contributor

jahorton commented Feb 27, 2025

I just realized one potential angle: a held backspace fires somewhat rapidly, though it shouldn't be rapidly enough to force the base context of a multitap out of the cache.

That said, once a held backspace is held, there's currently nothing to prevent someone from multitapping on a different key while that held backspace repeatedly fires. (Say... to reach the caps layer?) Inspecting the gesture model, we don't even auto-stop backspace repetition once a new, separate keystroke starts... and we probably should do so to be safe. (Granted, the fact that we haven't seen obvious issues from this is encouraging!)


Compare the special-key-end gesture spec with that of initial-tap.

initial-tap:

export function initialTapModel(params: FullGestureParams): GestureModel<any> {
return {
id: 'initial-tap',
resolutionPriority: 1,
contacts: [
{
model: {
...simpleTapContactModel(params),
pathInheritance: 'chop',
itemPriority: 1,
timer: {
duration: params.multitap.holdLength,
expectedResult: false
},
},
endOnResolve: true
}, {
model: instantContactResolutionModel(),
resetOnInstantFulfill: true
}
],

special-key-end:

export function specialKeyEndModel(params: FullGestureParams): GestureModel<any> {
return {
id: 'special-key-end',
resolutionPriority: 0,
contacts : [
{
model: {
...simpleTapContactModel(params),
itemChangeAction: 'resolve'
},
endOnResolve: true,
}
],

The second entry in initial-tap's contacts array causes the tap to instantly trigger when a new touch is received. No such mechanic is present for backspace here to auto-trigger the end of the held backspace in the same scenario, and we probably should - a multitap during a text-output backspace would act pretty weirdly.

jahorton pushed a commit that referenced this issue Feb 27, 2025
As identified in the analysis for #13344, held backspaces have not been cancelled when receiving new inputs.  It's better and cleaner to have it act similarly to normal keystrokes - receiving a new key input autocompletes any currently-held output keys.

The current, existing behavior also has the potential to get particularly messy with multitaps, which restore the context at their start.  If a backspace is held and then a multitap begins, each new tap of the multitap would undo the backspaces that triggered since the first tap of the multitap.
jahorton pushed a commit that referenced this issue Feb 27, 2025
As identified in the analysis for #13344, held backspaces have not been cancelled when receiving new inputs.  It's better and cleaner to have it act similarly to normal keystrokes - receiving a new key input autocompletes any currently-held output keys.

The current, existing behavior also has the potential to get particularly messy with multitaps, which restore the context at their start.  If a backspace is held and then a multitap begins, each new tap of the multitap would undo the backspaces that triggered since the first tap of the multitap.
jahorton pushed a commit that referenced this issue Feb 27, 2025
To facilitate debugging cases where gestures don't work as intended, this adds sanitized logging for keystrokes resulting for gestures.  These are only to be output when a relevant error or warning is emitted.

This is primarily being added to facilitate investigation of #13344.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

1 participant