Skip to content

fix(core): render hovers in the document of their target element - #17934

Open
safisa wants to merge 2 commits into
eclipse-theia:masterfrom
safisa:fix-hover-secondary-window
Open

fix(core): render hovers in the document of their target element#17934
safisa wants to merge 2 commits into
eclipse-theia:masterfrom
safisa:fix-hover-secondary-window

Conversation

@safisa

@safisa safisa commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What it does

Fixes #17933: render hovers in the ownerDocument of their target element, so tooltips for widgets moved to a secondary window appear next to the hovered element instead of detached in the main window.

  • Append the hover host to target.ownerDocument.body and compute the viewport metrics from that document; register the dismissing mousedown listener there and wait for an animation frame in the target's window.
  • Guard hidePopover() against documents that are no longer fully active — closing the secondary window while a hover is open used to throw InvalidStateError on the next hover and break all hovers until reload.
  • Add HoverPosition.fits and fall back to the perpendicular direction when a hover fits on neither side of its target (e.g. full-width items in a narrow secondary window), so target and hover both stay visible; clamp left/right hovers into the viewport as a last resort.

Behavior in the main window is unchanged. Adds unit tests for the new behavior (hover-service.spec.ts).

How to test

  1. Start the browser example, open the AI Chat view and move it to a secondary window.
  2. Hover a chat session item and a tool call row in a chat: the tooltip appears inside the secondary window next to the hovered element (above/below it when there is no room beside it).
  3. Close the secondary window while a tooltip is shown: hovers in the main window keep working, without console errors.
  4. Unit tests: npx lerna run test --scope @theia/core (or npx mocha packages/core/lib/browser/hover-service.spec.js after compiling).

Follow-ups

None.

Breaking changes

  • This PR introduces breaking changes and requires careful review. If yes, the breaking changes section in the changelog has been updated.

Attribution

Review checklist

Reminder for reviewers

@github-project-automation github-project-automation Bot moved this to Waiting on reviewers in PR Backlog Aug 20, 2026

@ndoschek ndoschek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for working on this! I tested the change with different views and editors and the intermediate issue you describe seems to be fixed, the tooltip appear now on the secondary window where it is expected.

Although I found two blocking problems:

  1. Flickering: with a view or editor open in a secondary window, hovering the editor tab now shows the tooltip in the secondary window, but it flickers heavily regardless of the enhanced preview setting. This should be fixed.

  2. Application Crash:
    Steps to reproduce (using an editor, but it is not editor-specific, it also happens with views such as the AI chat view):

    • Open an editor in a secondary window.
    • Hover the editor tab.
    • Without leaving the tab, close the editor via its "x" (which brings you back to the main application).
    • Now move a bit in the application, either opening another editor switching to another view. Just moving the mouse a little around the app is enough to crash the whole application after a few moments.

The only thing I can observe in the backend log is:

root INFO socket closed
Error sending from webFrameMain:  Error: Render frame was disposed before WebFrameMain could be accessed
    at WebFrameMain.send (node:electron/js2c/browser_init:2:109572)
    at WebContents.send (node:electron/js2c/browser_init:2:93779)
    at Object.sendWindowEvent (<theia>/examples/electron/lib/backend/electron-main.js:151668:12)
    at BrowserWindow.<anonymous> (<theia>/examples/electron/lib/backend/electron-main.js:152288:48)
    at BrowserWindow.emit (node:events:521:24)
Error sending from webFrameMain:  Error: Render frame was disposed before WebFrameMain could be accessed
    at WebFrameMain.send (node:electron/js2c/browser_init:2:109572)
    at WebContents.send (node:electron/js2c/browser_init:2:93779)
    at <theia>/examples/electron/lib/backend/electron-main.js:151707:14
    at new Promise (<anonymous>)
    at Object.requestClose (<theia>/examples/electron/lib/backend/electron-main.js:151700:16)
    at TheiaElectronWindow.checkSafeToStop (<theia>/examples/electron/lib/backend/electron-main.js:151877:53)
    at TheiaElectronWindow.handleStopRequest (<theia>/examples/electron/lib/backend/electron-main.js:151865:77)
    at BrowserWindow.<anonymous> (<theia>/examples/electron/lib/backend/electron-main.js:151840:16)
    at BrowserWindow.emit (node:events:521:24)

If you close the secondary window via the secondary windows' native close button, the editor is not closed but moves back to the main application and I cannot observe the crash in this case.
Also, I tested the same flow on current master and the crash does not occur there. Could you have another look please?

@github-project-automation github-project-automation Bot moved this from Waiting on reviewers to Waiting on author in PR Backlog Aug 20, 2026
@safisa

safisa commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Both issues should be fixed now (fb530fd):

  1. Flickering: the hover host was briefly visible and hit-testable at (0,0) before being positioned; in a secondary window that covered the hovered tab, kicked it out of the hover chain and re-triggered mouseenter hovers in an endless show/hide loop. The host is now kept hidden until positioned. Measured in the browser example: ~12 re-shows/second before, exactly 1 after.
  2. Crash: the singleton hover host was adopted into the secondary window's document and outlived it; after closing the window, the next hover touched DOM of a disposed document, killing the Electron renderer. The host is now created per document and never adopted across documents, and hovers are cancelled on pagehide of their hosting window. Could you re-check the crash scenario on Electron?

Unit tests added for all of the above.

@safisa
safisa requested a review from ndoschek August 24, 2026 08:50
@ndoschek ndoschek mentioned this pull request Aug 24, 2026
16 tasks
Hovers were always appended to the main window's document and positioned
with viewport metrics of the main window, so tooltips for widgets moved
to a secondary window appeared detached in the main window. Render and
position the hover in the target's ownerDocument instead, listen for
dismissing mousedown there, and guard hidePopover() against documents
that are no longer fully active (secondary window closed while a hover
is open).

Also fall back to the perpendicular direction when a hover fits on
neither side of its target (e.g. full-width items in a narrow secondary
window), and clamp left/right hovers into the viewport as a last resort.
@safisa
safisa force-pushed the fix-hover-secondary-window branch from fb530fd to fdbae0e Compare August 27, 2026 12:54
@ndoschek

Copy link
Copy Markdown
Member

Thanks for the update @safisa! I cannot reproduce the crash anymore 👍
Unfortunately I still see an issue in the secondary window though: the tooltip is staying in a secondary window after moving the pointer off the target, and repeating it stacks up more boxes. The tooltip will be canceled on another interaction in the secondary window.
It would be great to have tests for the dismissal paths in a secondary window (mouse-out and mousedown), since that is where the above slips through.

Also, the HoverPosition.fits fallback changes positioning in the main window too, a hover that fits neither above nor below now flips to the side. Is that intended?
And a small thing: the commit message still mentions the flicker fix, which is not part of the diff anymore.

- create the hover host in the document it is shown in and never adopt it
  across documents; cancel the hover on pagehide of the hosting window and
  guard against closed windows, so hovers no longer break (or crash the
  Electron renderer) after closing a secondary window with an open hover
- resolve the dismissal listeners and unRenderHover against the host of the
  current hover instead of recreating a host for the main document, so
  hovers in a secondary window are dismissed on mouse-out and mousedown
  instead of piling up
- do not let a superseded render reposition, reveal, or leak css classes
  into the hover that replaced it
@safisa
safisa force-pushed the fix-hover-secondary-window branch from fdbae0e to b99a079 Compare August 30, 2026 14:19
@safisa

safisa commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

The remaining issues should be addressed now (b99a079):

  • Stuck/stacking tooltips: the hoverHost getter always resolved against the main window's document and recreated the host on a document mismatch, so during a secondary-window hover the dismissal listeners silently swapped in a fresh, never-rendered main-document host — cancelHover then tore down the wrong element and the rendered popover was orphaned, with each further hover adding another box. The getter now returns the host of the current hover, so mouse-out and mousedown dismissal operate on the host that is actually shown.
  • Tests: added specs for both dismissal paths in a secondary window (mouse-out and mousedown), plus one asserting repeated hovers never pile up hosts — it reproduced the exact stacking before the fix.
  • HoverPosition.fits fallback in the main window: intended. It only kicks in when the hover fits neither in the requested direction nor its inverse — a case where master renders the hover clipped off-screen or over its target — and it only flips if the perpendicular direction actually fits. Main-window hovers are unaffected except where they were previously unusable. Happy to gate it to secondary windows if you prefer, though.
  • Commit message: reworded — the flicker fix landed on master separately with the fix for AI Sessions view: the session tooltip doesn't open and causes flickering #17930, so it is no longer part of this diff.

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

Labels

None yet

Projects

Status: Waiting on author

Development

Successfully merging this pull request may close these issues.

Hovers are misplaced for widgets moved to a secondary window

2 participants