Dead key input fails on first character on Firefox

Hello! How’s it going?

I’ve noticed a problem with WhatsApp Web in Firefox. When I use a dead key as the first letter (for example, when trying to type “árbol”, which is the Spanish word for “tree”) the letter disappears completely, although it works fine if it’s not the first letter (for example, “información”, which is the Spanish word for “information”).

A workaround is to launch Firefox as follows: GTK_IM_MODULE=ibus firefox (so it’s also possible to set the environment variable in /etc/environment or /etc/environment.d/). With this solution, I can use accents correctly, but the preedit is no longer visible.

The most curious thing is that if I change the Virtual Keyboard in the KDE settings from “Plasma Keyboard” to “IBus” everything is fixed, and even the preedit works correctly.

While it’s not a major issue (since I can use IBus just fine as an input method), I’d like to diagnose the problem a bit better; for example, to determine whether it’s a configuration issue in Fedora, Firefox, or Plasma Keyboard.

Environment:
Fedora 44 KDE (also tested on Fedora 43 KDE with Plasma Keyboard)
Display Server: Wayland
Window Manager: KWin

Any suggestions on where I might start looking?

Thanks!

For your troubleshooting, I can only add that this doesn’t reproduce in GNOME (Silverblue 43) using Firefox Flatpak from Flathub. Accent keys using keyboard layout English (US, alt. intl.).

Indeed, I cannot reproduce the issue on GNOME (Fedora 44) which uses IBus.

Thanks for your feedback!

1 Like

I tried to debug this issue with a simple JavaScript script to log some events. While I cannot reproduce the exact same issue (i.e., the key “disappearing” from the input) I am able to see some inconsistencies between IBus and Plasma Keyboard.

The script:

<!DOCTYPE html>
<html>

<body>
	<textarea id="k" rows="6" cols="60"></textarea>
	<br />
	<button id="dump">Dump</button>

	<script>
		const events = [];

		function snapshot(event)
		{
			const el = event.target;
			const e = {
				type: event.type,
				key: event.key,
				code: event.code,
				which: event.which,
				keyCode: event.keyCode,
				charCode: event.charCode,
				data: event.data ?? null,
				inputType: event.inputType ?? null,
				composed: event.composed,
				isComposing: event.isComposing,
				value: el?.value ?? null,
				selectionStart: el?.selectionStart ?? null,
				selectionEnd: el?.selectionEnd ?? null,
			};

			console.log(e);
			events.push(e);
		}

		const k = document.getElementById('k');
		k.value = '';

		['keydown', 'keypress', 'keyup', 'beforeinput', 'input', 'change', 'compositionstart', 'compositionupdate', 'compositionend']
			.forEach((type) => k.addEventListener(type, snapshot));

		k.focus();

		document.getElementById('dump').addEventListener('click', () => console.log(JSON.stringify(events, null, 2)));
	</script>
</body>

</html>

Results:

Firefox: IBus Wayland (KDE)
[
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionstart",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "´",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionend",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "change",
		"data": null,
		"inputType": null,
		"composed": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	}
]
Firefox: Plasma Keyboard (KDE)
[
	{
		"type": "keydown",
		"key": "Dead",
		"code": "BracketLeft",
		"which": 0,
		"keyCode": 0,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionstart",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "keydown",
		"key": "Dead",
		"code": "BracketLeft",
		"which": 0,
		"keyCode": 0,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "´",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "Dead",
		"code": "BracketLeft",
		"which": 0,
		"keyCode": 0,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "a",
		"code": "KeyA",
		"which": 65,
		"keyCode": 65,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "keydown",
		"key": "a",
		"code": "KeyA",
		"which": 65,
		"keyCode": 65,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionend",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "KeyA",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "á",
		"inputType": "insertText",
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertText",
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "a",
		"code": "KeyA",
		"which": 65,
		"keyCode": 65,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "change",
		"data": null,
		"inputType": null,
		"composed": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	}
]
Firefox: IBus Wayland (GNOME)
[
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionstart",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "´",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "Dead",
		"code": "BracketLeft",
		"which": 0,
		"keyCode": 0,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionend",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "a",
		"code": "KeyA",
		"which": 65,
		"keyCode": 65,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "change",
		"data": null,
		"inputType": null,
		"composed": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	}
]

The Plasma Keyboard test has some inconsistencies:

  • compositionupdate with an empty value field
  • beforeinput with an empty value field
  • input with an empty data field
  • compositionend with empty data/value fields

Same tests on Chromium:

Chromium: IBus Wayland (KDE)
[
	{
		"type": "compositionstart",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "´",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "´",
		"selectionStart": 0,
		"selectionEnd": 1
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 0,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionend",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "change",
		"data": null,
		"inputType": null,
		"composed": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	}
]
Chromium: Plasma Keyboard (KDE)
[
	{
		"type": "keydown",
		"key": "Process",
		"code": "BracketLeft",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionstart",
		"which": 0,
		"data": "",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "´",
		"inputType": null,
		"composed": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "",
		"selectionStart": 0,
		"selectionEnd": 0
	},
	{
		"type": "input",
		"which": 0,
		"data": "´",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "Dead",
		"code": "BracketLeft",
		"which": 219,
		"keyCode": 219,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keydown",
		"key": "Process",
		"code": "KeyA",
		"which": 229,
		"keyCode": 229,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionupdate",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "´",
		"selectionStart": 0,
		"selectionEnd": 1
	},
	{
		"type": "beforeinput",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "´",
		"selectionStart": 0,
		"selectionEnd": 1
	},
	{
		"type": "input",
		"which": 0,
		"data": "á",
		"inputType": "insertCompositionText",
		"composed": true,
		"isComposing": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "compositionend",
		"which": 0,
		"data": "á",
		"inputType": null,
		"composed": true,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "keyup",
		"key": "a",
		"code": "KeyA",
		"which": 65,
		"keyCode": 65,
		"charCode": 0,
		"data": null,
		"inputType": null,
		"composed": true,
		"isComposing": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	},
	{
		"type": "change",
		"data": null,
		"inputType": null,
		"composed": false,
		"value": "á",
		"selectionStart": 1,
		"selectionEnd": 1
	}
]

I tested on CachyOS and I can reproduce the same issue setting the Virtual Keyboard to either “None” or “Plasma Keyboard” (after installing it), but IBus works fine.

Could you raise a bug upstream in KDE, since this seems to be cross-distro?

I created a Proposed Common Issue to pull together the various Plasma Keyboard-related issues:

1 Like

I posted a comment on an existing issue in Mozilla’s Bugzilla (2004150 - Dead Keys composition (e.g., ' + vowel) fails and characters disappear in specific web input fields (e.g., WhatsApp Web, Copilot)), since at first I thought it might be a bug in Firefox itself; then I saw the following issue reported here: VsCode keeps typing like the key has not been released and a linked issue in KDE’s Discussion: Last pressed key prompt leaks, getting stuck while switching tabs in VSCode or any VSCode clones, only in Wayland - Help - KDE Discuss.

I was able to reproduce both issues with Plasma Keyboard as the input method, but it works fine using IBus.

I could raise a bug in KDE’s bugzilla as looks like both issues are related to Plasma Keybord. However, still interesting that I cannot reproduce the issue with Chromium.

Edit: Reported upstream: https://bugs.kde.org/show_bug.cgi?id=518141