Skip to content

Conversation

@vishesh92
Copy link
Member

@vishesh92 vishesh92 commented Dec 4, 2025

Description

Fixes #9940

Copilot Generated summary

This pull request enhances the reliability of modifier key handling in the noVNC UI, especially for VMware VMs using a websocket reverse proxy. It introduces logic to ensure all pressed modifier keys are released when the user closes the browser tab or navigates away, preventing stuck keys on the remote VM. The changes are grouped into modifier key management and event handling improvements.

Modifier key management:

  • Added a _modifierKeys configuration object to track modifier keys (Shift, Ctrl, Alt, Windows) and their associated keysyms, codes, and button IDs in the UI object.
  • Implemented _sendKeyUp, _releaseModifierKey, and _releaseAllModifierKeys methods to send key release events for individual or all modifier keys, updating UI state accordingly.

Event handling improvements:

  • Registered beforeunload and pagehide event listeners to trigger modifier key release logic when the user closes the tab or navigates away, ensuring the remote VM does not retain stuck modifier keys.
  • Added handleBeforeUnload and handlePageHide methods to execute modifier key release logic on relevant browser events.

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

Screenshots (if appropriate):

How Has This Been Tested?

How did you try to break this feature and the system with this change?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where modifier keys (Shift, Ctrl, Alt, Windows) remain stuck in the pressed state on VMware VMs when the browser tab/window closes unexpectedly, particularly when using a websocket reverse proxy. The solution adds event handlers to detect tab closure and release all pressed modifier keys before the connection is terminated.

Key changes:

  • Introduces a centralized _modifierKeys configuration object mapping modifier keys to their keysyms, codes, and button IDs
  • Adds beforeunload and pagehide event listeners to detect tab/window closure
  • Implements helper methods to release individual or all pressed modifier keys

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

UI.idleControlbar();
},

_sendKeyUp(keysym, code) {
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for UI.rfb before calling sendKey(). If the RFB connection is not initialized, this will throw a runtime error. Add a check like if (!UI.rfb) return; at the beginning of the method, consistent with other methods in this file (e.g., lines 1386, 1402, 1444).

Suggested change
_sendKeyUp(keysym, code) {
_sendKeyUp(keysym, code) {
if (!UI.rfb) return;

Copilot uses AI. Check for mistakes.
Comment on lines +1761 to +1773
_releaseModifierKey(keyName) {
const keyConfig = UI._modifierKeys[keyName];
if (!keyConfig) return false;

const btn = document.getElementById(keyConfig.buttonId);
if (!btn || !btn.classList.contains("noVNC_selected")) {
return false;
}

UI._sendKeyUp(keyConfig.keysym, keyConfig.code);
btn.classList.remove("noVNC_selected");
return true;
},
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for UI.rfb before calling _sendKeyUp(). Although _releaseAllModifierKeys() checks for UI.rfb existence, this method can be called independently and should be defensive. Add a check like if (!UI.rfb) return false; at the beginning of the method.

Copilot uses AI. Check for mistakes.
return keysReleased;
},


Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Extra blank line. Remove one of the blank lines to maintain consistency with the rest of the codebase, which uses single blank lines between methods.

Suggested change

Copilot uses AI. Check for mistakes.

// Release all currently pressed modifier keys
_releaseAllModifierKeys() {
if (!UI.rfb || UI.rfb._rfbConnectionState !== 'connected') {
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing the private property _rfbConnectionState directly breaks encapsulation. The RFB class may change this internal implementation in the future. Consider using a public API method to check the connection state, or checking if UI.connected is true instead, which is already used throughout the codebase to track connection status.

Suggested change
if (!UI.rfb || UI.rfb._rfbConnectionState !== 'connected') {
if (!UI.connected) {

Copilot uses AI. Check for mistakes.
Comment on lines 1791 to 1795
if (UI.rfb._shiftPressed) {
const shiftConfig = UI._modifierKeys.shift;
UI._sendKeyUp(shiftConfig.keysym, shiftConfig.code);
keysReleased = true;
}
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing the private property _shiftPressed directly breaks encapsulation. This internal RFB state may not be reliable or could change in future versions. Additionally, there's already logic to release shift via the button state check above, making this additional check potentially redundant.

Suggested change
if (UI.rfb._shiftPressed) {
const shiftConfig = UI._modifierKeys.shift;
UI._sendKeyUp(shiftConfig.keysym, shiftConfig.code);
keysReleased = true;
}

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Dec 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 17.56%. Comparing base (4379666) to head (e39eb24).

Additional details and impacted files
@@             Coverage Diff              @@
##              4.22   #12187       +/-   ##
============================================
+ Coverage     3.58%   17.56%   +13.97%     
- Complexity       0    15545    +15545     
============================================
  Files          445     5910     +5465     
  Lines        37536   529128   +491592     
  Branches      6905    64636    +57731     
============================================
+ Hits          1346    92919    +91573     
- Misses       36024   425753   +389729     
- Partials       166    10456    +10290     
Flag Coverage Δ
uitests 3.58% <ø> (ø)
unittests 18.62% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vishesh92
Copy link
Member Author

@blueorangutan package

@blueorangutan
Copy link

@vishesh92 a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 15916

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants