Skip to content

Firefox: mathVirtualKeyboard.show() blocks for ~1.2s+ even when the keyboard DOM already exists #3047

Description

@Shrookin

Firefox: mathVirtualKeyboard.show() blocks for ~1.2s+ even when the keyboard DOM already exists

Summary

We are seeing a Firefox-only performance issue in MathLive's virtual keyboard path.

  • MathLive version: 0.108.3
  • Firefox version: 152.0.3
  • OS: Windows 11, build 26100
  • Integration mode: mathVirtualKeyboardPolicy = "manual"
  • Integration behavior:
    • call window.mathVirtualKeyboard.show() on mathfield focus
    • call window.mathVirtualKeyboard.hide() on blur

Chrome does not reproduce the same issue.

Observed Behavior

In Firefox, window.mathVirtualKeyboard.show() blocks synchronously for about 1.2s to 3.3s.

This affects:

  • first open
  • reopen after blur
  • reopen after switching between mathfields if a fresh show() call happens

We can make hide feel immediate in app code, and we can also keep the keyboard visible across direct mathfield-to-mathfield transitions. Those app-level fixes improve UX, but they do not remove the main show() stall.

Strongest Evidence

We added logs immediately before and after window.mathVirtualKeyboard.show().

Typical timings:

  • before any workaround:
    • first opens around 2200ms to 3300ms
  • after a hidden warm-up (show() + hide() once in the background):
    • first real open still around 1623ms
    • later opens around 1437ms to 1869ms
  • after prebuilding the keyboard DOM without showing it:
    • prebuild itself took about 95ms
    • first real open still took about 1192ms
    • reopen after blur still took about 1228ms

The key point is:

  • before prebuild: keyboardFound: false
  • after prebuild: keyboardFound: true
  • on the next focus, show() is still slow even though the keyboard already exists

That strongly suggests the remaining cost is not just first-time DOM creation.

Why This Looks Like a MathLive show()-Path Issue

The timing is captured immediately around:

const t0 = performance.now();
window.mathVirtualKeyboard.show();
console.log(performance.now() - t0);

So the blocking time is inside MathLive's show() path itself.

Additional Observations

  • Firefox focus ordering around mathfield-to-mathfield transitions is less reliable than Chrome
  • We handled that locally by not hiding the keyboard when focus is clearly moving directly to another mathfield
  • When we avoid a fresh show() call and simply keep the existing keyboard visible, the transition is effectively immediate

That suggests the main remaining UX problem is tied specifically to show(), not to ordinary focus handoff.

Likely Hotspot in the Current Source

From local inspection of src/virtual-keyboard/virtual-keyboard.ts, the most likely hotspot appears to be synchronous work that still happens during show() even when _element already exists:

  • observer.observe(plate)
  • plate.offsetHeight / layout measurement
  • body padding adjustment
  • this.currentLayer = this.latentLayer
  • currentLayer -> render()
  • render() walks visible keycaps and rewrites their markup

I have not proven which of those is the dominant bottleneck yet, but the data now points more strongly at this area than at first-time keyboard construction.

Minimal Repro

I prepared a standalone repro HTML with:

  • MathLive 0.108.3 from CDN
  • two math-field elements
  • direct timing logs around show() and hide()
  • a hidden warm-up path
  • a prebuild path to separate creation cost from show() cost

Questions

  1. Is this a known Firefox performance issue in the virtual keyboard path?
  2. Even when the keyboard element already exists, should show() still be expected to block for around 1.2s in Firefox?
  3. Is show() doing expensive synchronous rerender work on every open by design?
  4. Is there a recommended way to pre-initialize the keyboard without still paying the reopen cost?
  5. Is there a recommended strategy to keep the keyboard mounted or visible across field-to-field transitions on Firefox?

Local Patch Candidate We Are Investigating

We also prepared a local candidate patch in a clone of the MathLive repo that tries to reduce redundant reopen work by:

  • avoiding currentLayer churn when the active layer is already correct
  • reusing a cached plate height before falling back to forced layout reads

This candidate builds, but we have not yet verified runtime improvement in Firefox.

So far this should be treated as a debugging lead, not as a validated fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions