Skip to content

feat: show screen picker on originating popout window and queue concurrent requests#3399

Merged
jeanfbrito merged 4 commits into
masterfrom
feat/SSGA-31-popout-screen-picker
Jul 10, 2026
Merged

feat: show screen picker on originating popout window and queue concurrent requests#3399
jeanfbrito merged 4 commits into
masterfrom
feat/SSGA-31-popout-screen-picker

Conversation

@jeanfbrito

@jeanfbrito jeanfbrito commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

SSGA-31

Screen share requests from popout windows (created by the webapp via window.open) share the server webview's session, so the desktop client always showed the screen picker on the main window regardless of which window originated the getDisplayMedia request. Concurrent requests were rejected outright (Electron overlaid pickers before that guard existed), unlike Chrome, which queues them and shows them one by one.

This PR shows the picker on the window that originated the request and queues concurrent requests FIFO.

Changes

  • Origin routing — the per-session setDisplayMediaRequestHandler now resolves the originating window from request.frame (resolveStandaloneOriginWindow). Requests from the server webview keep the existing root-window picker; requests from a standalone popout window open a picker on that popout.
  • Standalone picker window — new screenPickerWindow.ts (main) + screen-picker-window.tsx (renderer entry) + screen-picker-window.html + rollup entry. A fixed-size child BrowserWindow parented to the popout, reusing the existing ScreenSharePicker component; IPC channel names passed via query params. All navigation and window.open denied; CSP locked to default-src 'self'.
  • FIFO queueScreenSharingRequestTracker queues concurrent requests instead of answering {video: false}. Each request gets a cancel() handle; queued requests whose popout closed are skipped (isStillValid); the 60s timeout starts when a picker is shown, not when enqueued.
  • Teardown — new ScreenSharingRequestTracker.cancelAll() settles the active request and drains the queue (closing any popout picker window) without starting the next one. All four videoCallScreenSharingTracker reset sites in videoCallWindow/ipc.ts use it — the previous silent cleanup() would leave a popout's picker window orphaned and its getDisplayMedia promise hanging when the call window closed.
  • ProvidershandleDisplayMediaRequest gains an optional originWindow parameter, threaded through InternalPickerProvider. The Linux portal picker path is unchanged (OS-level dialog, origin-agnostic).
  • Video call window — popups opened from the call window (blob:/about:) get the same origin routing via the shared popoutPickerRequest.ts helper.
  • Deny semantics — all deny paths now answer callback(null). Electron only accepts null as a deny (DisplayMediaDeviceChosen in electron_browser_context.cc); the previous {video: false} shape — pre-existing on master and hidden by as any casts — throws a TypeError at runtime, surfacing as an unhandled rejection in main and leaving the renderer's getDisplayMedia promise hanging until Chromium times out. DisplayMediaCallback is now typed nullable, with casts only at the boundary where Electron's raw callback (whose .d.ts omits null) crosses into it. Surfaced by live QA of the cancel flow.
  • Settlement hardening — the tracker no longer marks itself idle while desktopCapturer.getSources validation is in flight (concurrent requests queue instead of draining the queue or double-starting), cancellation during validation can no longer double-settle a callback, and cleanup() settles the active entry instead of silently dropping it. Regression tests cover cancel-during-validation, queue-during-validation, and cleanup mid-request.

Validation

  • yarn lint, npx tsc --noEmit: clean
  • yarn test: 70 suites, 1148 passed (new specs for queue behavior, cancelAll, and origin resolution)
  • yarn build: compiles

Manually QA'd against a live server: picker on originating popout, FIFO queueing, and cancel/deny paths (no unhandled rejections; webapp receives NotAllowedError).

Manual test steps

  1. Open a workspace, start a video call, pop it out via the webapp popout button.
  2. Share screen from the popout → picker should appear centered on the popout window, not the main window.
  3. Trigger a second share request while the first picker is open → it should queue and appear after the first resolves, not overlay.
  4. Close the popout while its picker is open → picker window closes, no hung request.
  5. Share screen from the main window → picker appears on the main window as before.
  6. Linux/Wayland: portal (OS) picker behavior unchanged.

Summary by CodeRabbit

  • New Features
    • Added a dedicated standalone screen-sharing picker window for popout/standalone flows.
    • Implemented origin-aware picker routing for video calls and shared/server view requests.
  • Bug Fixes
    • Improved screen-sharing request queuing, cancellation, and cleanup (including draining queued requests on teardown).
    • Standardized denial/settled results to return null instead of { video: false }.
  • Tests
    • Expanded Jest coverage for request queuing/cancellation, cleanup/drain behavior, and origin-window resolution.

…rrent requests

Screen share requests from popout windows (webapp window.open) share the
server webview session, so the per-session display-media handler always
showed the picker on the main window and rejected concurrent requests.

- Resolve the originating window from request.frame; popout-originated
  requests open a dedicated picker window parented to the popout
- Queue concurrent requests FIFO in ScreenSharingRequestTracker instead
  of rejecting them, matching Chrome's one-at-a-time behavior
- Add cancelAll() teardown so closing the video call window settles
  in-flight requests and closes popout picker windows
- Thread originWindow through picker providers; portal (Linux) path
  unchanged

SSGA-31
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds queued and cancelable screen-sharing requests, a standalone Electron picker window with HTML and React entrypoints, and origin-aware routing through server-view and video-call IPC flows.

Changes

Screen Picker Window and Queued Request Tracking

Layer / File(s) Summary
Queued request tracking
src/screenSharing/ScreenSharingRequestTracker.ts, src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
Adds FIFO queueing, per-request settlement, cancellation handles, validity checks, queue draining, and lifecycle coverage.
Standalone picker window
src/public/screen-picker-window.html, src/screenSharing/screenPickerWindow.ts, src/screenSharing/screen-picker-window.tsx, rollup.config.mjs
Adds the picker window shell, Electron window factory, React theme/i18n bootstrap, and Rollup entry.
Popout request routing and origin resolution
src/screenSharing/popoutPickerRequest.ts, src/screenSharing/serverViewScreenSharing.ts, src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
Routes requests through standalone picker windows and resolves their originating BrowserWindow from web frames.
Provider and callback contracts
src/screenSharing/screenPicker/types.ts, src/screenSharing/screenPicker/providers/*, src/screenSharing/screenPicker/__tests__/*
Threads optional origin windows through providers and changes denied display-media callbacks to use null.
Video call IPC routing and cleanup
src/videoCallWindow/ipc.ts, src/videoCallWindow/main/ipc.main.spec.ts
Adds origin-based picker routing, dedicated channel mappings, and cancelAll() cleanup during teardown and picker handoff.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Webview
  participant IPC as videoCallWindow/ipc.ts
  participant Resolver as resolveStandaloneOriginWindow
  participant Picker as requestViaPickerWindow
  participant Window as ScreenPickerWindow
  participant Tracker as ScreenSharingRequestTracker

  Webview->>IPC: display-media request
  IPC->>Resolver: resolve request frame
  Resolver-->>IPC: origin window or null
  IPC->>Picker: route foreign-origin request
  Picker->>Tracker: createRequest with validity and completion handlers
  Picker->>Window: open picker with channel parameters
  Window-->>Picker: selection, denial, or close
  Picker->>Tracker: cancel or complete request
  Tracker-->>IPC: display-media result
Loading

Possibly related PRs

Suggested labels: type: feature

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: routing the screen picker to the originating popout window and queueing concurrent requests.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • SSGA-31: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

macOS installer download

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/screenSharing/ScreenSharingRequestTracker.ts (1)

46-70: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

cleanup() does not settle the active entry before clearing it.

cleanup() sets this.activeEntry = null without calling the active entry's callback or onDone hook. If cleanup() is invoked while a request is active (e.g., by external code or a future caller), the active request's callback will never fire, leaving the caller hanging indefinitely. Compare with cancelAll() (line 244), which correctly settles the active entry via cancelActiveEntry before draining the queue.

The test at ScreenSharingRequestTracker.main.spec.ts lines 510-526 also does not verify the active entry's callback after cleanup(), masking this gap.

🔧 Proposed fix: settle active entry in cleanup()
   cleanup(): void {
+    if (this.activeEntry && !this.activeEntry.settled) {
+      this.activeEntry.settled = true;
+      this.activeEntry.cb({ video: false } as any);
+      this.activeEntry.options?.onDone?.();
+    }
+
     if (this.activeListener) {
       ipcMain.removeListener(this.responseChannel, this.activeListener);
       this.activeListener = null;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/ScreenSharingRequestTracker.ts` around lines 46 - 70,
`cleanup()` in `ScreenSharingRequestTracker` clears `this.activeEntry` without
settling the in-flight request, so update it to reuse the same active-entry
settlement path used by `cancelAll()`/`cancelActiveEntry` before nulling state.
Ensure the active entry callback and `onDone` are invoked when `cleanup()` runs,
then drain the remaining queue as it does today. Also update the `cleanup()`
spec in `ScreenSharingRequestTracker.main.spec.ts` to assert the active request
is settled, not just the queued entries.
🧹 Nitpick comments (2)
src/screenSharing/ScreenSharingRequestTracker.ts (1)

95-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

finishActive relies on the caller having already called markComplete().

finishActive does not call markComplete() itself, depending on every call site to do it first. All current call sites (listener lines 144, timeout line 191) do call markComplete() before finishActive, so this is correct today. However, it's fragile — a future call site that forgets markComplete() would leave isPending stale and activeEntry dangling. Consider calling markComplete() inside finishActive (or documenting the precondition) to make the contract self-enforcing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/ScreenSharingRequestTracker.ts` around lines 95 - 99,
`finishActive` in `ScreenSharingRequestTracker` currently assumes callers have
already invoked `markComplete()`, which makes the completion contract fragile.
Update `finishActive(entry: QueueEntry)` to enforce that contract itself by
calling `markComplete()` before setting `settled`, invoking `onDone`, and
advancing with `processNext()`, or otherwise add a clear precondition near
`finishActive` and its call sites (`listener` and timeout handling) if you keep
the current behavior.
src/screenSharing/screenPickerWindow.ts (1)

60-67: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider blocking all navigation instead of allowing file:// URLs.

The picker window should only ever display its initial HTML. Allowing file:// navigation means the window could navigate to any local file, which would execute with nodeIntegration: true if it contains scripts. Blocking all navigation is safer since there is no legitimate navigation use case for this window.

🔒 Proposed: block all navigation
   screenPickerWindow.webContents.on(
     'will-navigate',
-    (event: Event, url: string) => {
-      if (!url.startsWith('file://')) {
-        event.preventDefault();
-      }
+    (event: Event) => {
+      event.preventDefault();
     }
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/screenPickerWindow.ts` around lines 60 - 67, The screen
picker window navigation guard in screenPickerWindow.webContents currently
allows file:// URLs, which leaves an unsafe local-file navigation path open.
Update the will-navigate handler in screenPickerWindow so it prevents every
navigation attempt unconditionally, rather than checking the URL prefix, since
this window should only ever show its initial HTML. Keep the fix scoped to the
existing screenPickerWindow setup and its webContents event listener.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/public/screen-picker-window.html`:
- Line 5: The current Content Security Policy in screen-picker-window HTML only
constrains script loading, so strengthen the policy by adding a restrictive
default-src alongside the existing script-src. Update the meta CSP tag in
screen-picker-window.html so default-src is explicitly set to a safe local-only
baseline, keeping the current script restriction intact and covering other
resource types as well.

In `@src/screenSharing/screenPickerWindow.ts`:
- Around line 14-22: openScreenPickerWindow currently calls parent.getBounds()
without verifying the BrowserWindow is still alive, which can throw if the
window is destroyed between the validation in startRequest and this callback.
Add a parent.isDestroyed() guard at the start of openScreenPickerWindow, before
any use of parent.getBounds(), and exit early or avoid creating the picker
window when the parent is gone. Keep the check alongside the existing parent
bounds logic so the unsafe access is prevented in this function.
- Around line 36-38: The screen picker window is still created with Node-enabled
renderer settings, which leaves the UI with unnecessary Node access. Update the
BrowserWindow configuration in screenPickerWindow so it uses a preload script
with contextBridge for the picker IPC actions, then switch webPreferences to
contextIsolation: true and nodeIntegration: false. Keep the existing picker flow
intact by moving any renderer-side Node usage behind the preload bridge.

---

Outside diff comments:
In `@src/screenSharing/ScreenSharingRequestTracker.ts`:
- Around line 46-70: `cleanup()` in `ScreenSharingRequestTracker` clears
`this.activeEntry` without settling the in-flight request, so update it to reuse
the same active-entry settlement path used by `cancelAll()`/`cancelActiveEntry`
before nulling state. Ensure the active entry callback and `onDone` are invoked
when `cleanup()` runs, then drain the remaining queue as it does today. Also
update the `cleanup()` spec in `ScreenSharingRequestTracker.main.spec.ts` to
assert the active request is settled, not just the queued entries.

---

Nitpick comments:
In `@src/screenSharing/screenPickerWindow.ts`:
- Around line 60-67: The screen picker window navigation guard in
screenPickerWindow.webContents currently allows file:// URLs, which leaves an
unsafe local-file navigation path open. Update the will-navigate handler in
screenPickerWindow so it prevents every navigation attempt unconditionally,
rather than checking the URL prefix, since this window should only ever show its
initial HTML. Keep the fix scoped to the existing screenPickerWindow setup and
its webContents event listener.

In `@src/screenSharing/ScreenSharingRequestTracker.ts`:
- Around line 95-99: `finishActive` in `ScreenSharingRequestTracker` currently
assumes callers have already invoked `markComplete()`, which makes the
completion contract fragile. Update `finishActive(entry: QueueEntry)` to enforce
that contract itself by calling `markComplete()` before setting `settled`,
invoking `onDone`, and advancing with `processNext()`, or otherwise add a clear
precondition near `finishActive` and its call sites (`listener` and timeout
handling) if you keep the current behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 828116c1-8a81-4f36-8637-c4b3172c7215

📥 Commits

Reviewing files that changed from the base of the PR and between a459f10 and 47d0832.

📒 Files selected for processing (14)
  • rollup.config.mjs
  • src/public/screen-picker-window.html
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/videoCallWindow/ipc.ts
  • src/videoCallWindow/main/ipc.main.spec.ts
📜 Review details
⚠️ CI failures not shown inline (2)

GitHub Actions: Build pull request artifacts / build (ubuntu-latest, linux): feat: show screen picker on originating popout window and queue concurrent requests

Conclusion: failure

View job details

##[group]Run SNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit)
 �[36;1mSNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit)�[0m
 �[36;1mif [ -z "$SNAP_FILE" ]; then�[0m
 �[36;1m  echo "::error::Snap file not found in dist/"�[0m

GitHub Actions: Build pull request artifacts / 0_build (ubuntu-latest, linux).txt: feat: show screen picker on originating popout window and queue concurrent requests

Conclusion: failure

View job details

##[group]Run SNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit)
 �[36;1mSNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit)�[0m
 �[36;1mif [ -z "$SNAP_FILE" ]; then�[0m
 �[36;1m  echo "::error::Snap file not found in dist/"�[0m
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use TypeScript for all new code unless explicitly told otherwise
Use optional chaining with fallbacks for platform-specific APIs instead of mocking when possible. Example: const uid = process.getuid?.() ?? 1000;

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,ts}: MANDATORY: Use Fuselage components for all UI work. Only create custom components when Fuselage doesn't provide what's needed
Import UI components from @rocket.chat/fuselage and check Theme.d.ts for valid color tokens
Use React functional components with hooks
Use PascalCase for component file names

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.spec.ts file naming for Renderer process tests

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.main.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.main.spec.ts file naming for Main process tests

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.{spec.ts,main.spec.ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Only mock platform-specific APIs when defensive coding isn't possible. Linux-only APIs requiring mocks: process.getuid(), process.getgid(), process.geteuid(), process.getegid()

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Redux actions must follow FSA (Flux Standard Action) pattern
Avoid unnecessary comments — write self-documenting code through clear naming
Always verify libraries by checking official docs and .d.ts files in node_modules/. Never assume props, tokens, or APIs work without verification
Avoid subjective descriptors ('smart', 'excellent', 'dumb') in documentation and comments
Use measurable descriptions in code documentation: 'reduced memory usage', 'improved by X%' instead of subjective claims
NEVER invent metrics — don't include estimated time spent or speculated user counts. Only include numbers from actual logs, error messages, or documented sources

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
🔇 Additional comments (12)
src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts (1)

82-97: LGTM!

Also applies to: 99-117, 119-138, 140-162, 164-189, 191-216, 220-274, 528-578

src/screenSharing/screen-picker-window.tsx (2)

21-37: LGTM!

Also applies to: 39-54, 56-68, 70-93, 95-112


1-2: 📐 Maintainability & Code Quality

Themes has no root export The deep type import is the available declaration, so there’s nothing to switch in @rocket.chat/fuselage’s public barrel.

			> Likely an incorrect or invalid review comment.
rollup.config.mjs (1)

270-303: LGTM!

src/screenSharing/popoutPickerRequest.ts (1)

14-42: LGTM!

The bidirectional cleanup design is sound: the picker closed event cancels the tracker handle, and onDone closes the picker window. The handle?.cancel() call in the closed listener is safe because createRequest returns synchronously before any async closed event can fire, so handle is always assigned by the time the listener executes.

src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts (1)

1-90: LGTM!

Test coverage is thorough — null/undefined frames, missing fromFrame results, webview guest exclusion via hostWebContents, and the standalone-window happy path are all covered with correct mock assertions.

src/screenSharing/screenPicker/types.ts (1)

1-2: LGTM!

The optional originWindow?: BrowserWindow parameter is correctly added to the interface, and the JSDoc clearly explains its purpose. PortalPickerProvider omitting the parameter remains valid TypeScript since optional params can be dropped in implementations.

Also applies to: 23-30

src/screenSharing/screenPicker/providers/InternalPickerProvider.ts (1)

1-2: LGTM!

The handleRequestFn type, setHandleRequestHandler signature, and handleDisplayMediaRequest all correctly thread the optional originWindow through to the main-process handler.

Also applies to: 23-25, 34-37, 50-55

src/screenSharing/screenPicker/providers/PortalPickerProvider.ts (1)

13-14: LGTM!

The added comments clearly explain why originWindow is irrelevant for the OS-level portal dialog. The signature omitting the parameter is valid TypeScript for an optional interface param.

src/screenSharing/serverViewScreenSharing.ts (1)

1-10: LGTM!

resolveStandaloneOriginWindow correctly returns null for webview guests (via hostWebContents check) and returns the owning BrowserWindow for standalone windows. createServerViewPickerHandler properly routes through requestViaPickerWindow when a valid originWindow exists and falls back to the root-window path otherwise. The handleServerViewDisplayMediaRequest signature correctly threads originWindow through to the provider.

Also applies to: 22-63, 92-92, 117-123, 164-179

src/videoCallWindow/ipc.ts (1)

21-29: LGTM!

The cancelAll() replacements in cleanup/close/open-screen-picker paths are correct — they settle both active and queued requests and invoke onDone to close any orphaned popout picker windows. The createInternalPickerHandler correctly routes popout-originated requests (valid originWindowvideoCallWindow) through requestViaPickerWindow while preserving the call-window picker path for in-call requests. The setupDisplayMediaHandler origin resolution and shared-session routing logic (fromCallWindow check → server-view vs call-window picker) is sound.

Also applies to: 85-90, 215-218, 265-283, 347-380, 789-790, 829-830, 1194-1199

src/videoCallWindow/main/ipc.main.spec.ts (1)

162-168: LGTM!

The cancelAll = jest.fn() addition correctly matches the new tracker API used by the IPC teardown paths.

Comment thread src/public/screen-picker-window.html Outdated
Comment thread src/screenSharing/screenPickerWindow.ts
Comment thread src/screenSharing/screenPickerWindow.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts (2)

99-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing regression test for cancellation during in-flight async validation.

None of the FIFO/queue tests exercise handle.cancel() (or an isStillValid failure) while desktopCapturer.getSources() is still pending for the active entry — this is exactly the scenario behind the double-settlement race flagged in ScreenSharingRequestTracker.ts (lines 126-176). Suggest adding a test that calls cancel() between issuing the listener response and resolving getSourcesMock, asserting cb is invoked only once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts` around lines
99 - 138, Add a regression test in ScreenSharingRequestTracker.main.spec.ts that
covers cancelling the active request while desktopCapturer.getSources() is still
pending in ScreenSharingRequestTracker.createRequest/handleResponse flow. Reuse
the existing ScreenSharingRequestTracker, getSourcesMock, and
getRegisteredListener setup, trigger the listener, call handle.cancel() (or
force isStillValid to fail) before resolving getSourcesMock, then resolve the
promise and assert the callback is invoked only once and the request is not
double-settled.

510-525: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test coverage gap: cleanup() behavior for an active (non-queued) entry isn't asserted.

This test only checks that queued entries are drained; it doesn't assert what happens to the already-active entry's cb/onDone when cleanup() runs mid-request. This matches the gap flagged in ScreenSharingRequestTracker.ts (cleanup() lines 58-70) — once that's fixed, add an assertion here that the active entry's cb/onDone are also invoked.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts` around lines
510 - 525, The cleanup test in ScreenSharingRequestTracker.main.spec.ts only
verifies queued requests are drained, but it misses the active entry handled by
ScreenSharingRequestTracker.cleanup(). Update this spec to assert that when
cleanup runs mid-request, the currently active request’s cb and onDone are also
invoked, using the existing tracker.createRequest, cleanup, queuedCb, and
queuedOnDone setup to distinguish active versus queued behavior.
src/screenSharing/ScreenSharingRequestTracker.ts (1)

95-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider consolidating the repeated settle-and-advance pattern; cancelActiveEntry/processNext coupling is implicit.

The entry.cb({ video: false } as any); this.finishActive(entry); (or entry.options?.onDone?.()) pattern is repeated ~5 times across finishActive, cancelActiveEntry, processNext, and the listener/timeout bodies. Also, cancelActiveEntry() does not itself call processNext() — every caller must remember to call it separately (correctly done today in cancel(), intentionally skipped in cancelAll()), which is an easy invariant to break in future changes (as the critical bug above illustrates). Extracting a single settle(entry, result) helper, and making the active-vs-cancel-all advancement explicit (e.g. a parameter), would reduce the chance of another regression like the one flagged above.

Also applies to: 200-242

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/ScreenSharingRequestTracker.ts` around lines 95 - 124, The
settle-and-advance logic is duplicated across finishActive, cancelActiveEntry,
and processNext, and the implicit requirement to call processNext after
cancelActiveEntry is easy to miss. Refactor ScreenSharingRequestTracker by
extracting a single helper for settling a QueueEntry (invoked from finishActive,
cancelActiveEntry, and the validity-check path in processNext) and make the
“advance to next” behavior explicit via a parameter or separate helper. Keep the
existing semantics for cancel() versus cancelAll(), but ensure the helper
consistently handles entry.settled, entry.cb({ video: false } as any), and
entry.options?.onDone?.() in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts`:
- Around line 99-138: Add a regression test in
ScreenSharingRequestTracker.main.spec.ts that covers cancelling the active
request while desktopCapturer.getSources() is still pending in
ScreenSharingRequestTracker.createRequest/handleResponse flow. Reuse the
existing ScreenSharingRequestTracker, getSourcesMock, and getRegisteredListener
setup, trigger the listener, call handle.cancel() (or force isStillValid to
fail) before resolving getSourcesMock, then resolve the promise and assert the
callback is invoked only once and the request is not double-settled.
- Around line 510-525: The cleanup test in
ScreenSharingRequestTracker.main.spec.ts only verifies queued requests are
drained, but it misses the active entry handled by
ScreenSharingRequestTracker.cleanup(). Update this spec to assert that when
cleanup runs mid-request, the currently active request’s cb and onDone are also
invoked, using the existing tracker.createRequest, cleanup, queuedCb, and
queuedOnDone setup to distinguish active versus queued behavior.

In `@src/screenSharing/ScreenSharingRequestTracker.ts`:
- Around line 95-124: The settle-and-advance logic is duplicated across
finishActive, cancelActiveEntry, and processNext, and the implicit requirement
to call processNext after cancelActiveEntry is easy to miss. Refactor
ScreenSharingRequestTracker by extracting a single helper for settling a
QueueEntry (invoked from finishActive, cancelActiveEntry, and the validity-check
path in processNext) and make the “advance to next” behavior explicit via a
parameter or separate helper. Keep the existing semantics for cancel() versus
cancelAll(), but ensure the helper consistently handles entry.settled,
entry.cb({ video: false } as any), and entry.options?.onDone?.() in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 828116c1-8a81-4f36-8637-c4b3172c7215

📥 Commits

Reviewing files that changed from the base of the PR and between a459f10 and 47d0832.

📒 Files selected for processing (14)
  • rollup.config.mjs
  • src/public/screen-picker-window.html
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/videoCallWindow/ipc.ts
  • src/videoCallWindow/main/ipc.main.spec.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use TypeScript for all new code unless explicitly told otherwise
Use optional chaining with fallbacks for platform-specific APIs instead of mocking when possible. Example: const uid = process.getuid?.() ?? 1000;

Files:

  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,ts}: MANDATORY: Use Fuselage components for all UI work. Only create custom components when Fuselage doesn't provide what's needed
Import UI components from @rocket.chat/fuselage and check Theme.d.ts for valid color tokens
Use React functional components with hooks
Use PascalCase for component file names

Files:

  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Redux actions must follow FSA (Flux Standard Action) pattern
Avoid unnecessary comments — write self-documenting code through clear naming
Always verify libraries by checking official docs and .d.ts files in node_modules/. Never assume props, tokens, or APIs work without verification
Avoid subjective descriptors ('smart', 'excellent', 'dumb') in documentation and comments
Use measurable descriptions in code documentation: 'reduced memory usage', 'improved by X%' instead of subjective claims
NEVER invent metrics — don't include estimated time spent or speculated user counts. Only include numbers from actual logs, error messages, or documented sources

Files:

  • src/screenSharing/popoutPickerRequest.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/screen-picker-window.tsx
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/videoCallWindow/ipc.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.spec.ts file naming for Renderer process tests

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.main.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.main.spec.ts file naming for Main process tests

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.{spec.ts,main.spec.ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Only mock platform-specific APIs when defensive coding isn't possible. Linux-only APIs requiring mocks: process.getuid(), process.getgid(), process.geteuid(), process.getegid()

Files:

  • src/videoCallWindow/main/ipc.main.spec.ts
  • src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
🔇 Additional comments (29)
src/screenSharing/serverViewScreenSharing.ts (6)

1-10: LGTM!


33-44: LGTM!

resolveStandaloneOriginWindow correctly distinguishes a standalone popout (no hostWebContents) from a webview guest (has hostWebContents), consistent with Electron's documented hostWebContents semantics.


92-92: LGTM!


117-123: LGTM!


164-179: LGTM!


46-73: 🩺 Stability & Availability

Handle root-window lookup failures by settling the queued request. If getRootWindow() resolves to null/destroyed or rejects here, the active FIFO entry can stay pending and block later requests; use the tracker’s cancel/settle path instead of only logging.

src/videoCallWindow/ipc.ts (6)

21-29: LGTM!


85-90: LGTM!


215-218: LGTM!

Replacing cleanup() with cancelAll() in the window-cleanup, closed, and close paths correctly drains both the active entry and the queue so a popout-parented picker window isn't orphaned.

Also applies to: 789-790, 829-830


264-296: LGTM!

Correctly falls back to cb({ video: false }) when the video call window is unavailable, unlike the analogous fallback path in serverViewScreenSharing.ts.


347-380: LGTM!

Origin/popoutOrigin/fromCallWindow routing correctly separates in-call requests, main-server-webview requests (shared session), and genuine popout requests.


1194-1199: 🩺 Stability & Availability

cancelAll() may reject an active popout picker request. This handler clears the shared tracker before claiming video-call-window/screen-sharing-source-responded, so an in-flight requestViaPickerWindow could be force-closed if open-screen-picker races with it. If that preemption is intentional, keep it; otherwise queue this request behind the active one.

src/videoCallWindow/main/ipc.main.spec.ts (1)

162-169: LGTM!

src/screenSharing/ScreenSharingRequestTracker.ts (4)

8-39: LGTM!


185-194: LGTM!

Also applies to: 244-258


58-70: 🩺 Stability & Availability

Ensure cleanup() settles an in-flight request. If this path can run while activeEntry is set, settle it before clearing state; otherwise the getDisplayMedia() promise can hang.


126-176: 🩺 Stability & Availability

Potential stale callback after async source lookup src/screenSharing/ScreenSharingRequestTracker.ts:126-176entry.settled is only checked before await desktopCapturer.getSources(). If cancellation settles the entry while that call is pending, this branch can still invoke entry.cb(...) and finishActive(entry) for the same request. Re-check entry.settled after the await, or defer markComplete() until settlement.

src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts (1)

82-274: LGTM!

src/public/screen-picker-window.html (1)

1-33: LGTM!

src/screenSharing/screenPickerWindow.ts (2)

1-35: LGTM!

Also applies to: 40-75


36-39: 🔒 Security & Privacy

nodeIntegration: true + contextIsolation: false should stay limited to trusted local content. If this window can ever be reached by untrusted content, switch to a preload bridge with contextIsolation: true.

src/screenSharing/screen-picker-window.tsx (2)

39-54: 🎯 Functional Correctness

Verify PaletteStyleTag's selector prop against the installed Fuselage version's typings.

As per coding guidelines, library props should be verified against official docs/.d.ts files rather than assumed. Please confirm selector is a valid prop for PaletteStyleTag in @rocket.chat/fuselage@0.78.0 (check node_modules/@rocket.chat/fuselage/dist/components/PaletteStyleTag).
As per coding guidelines: "Always verify libraries by checking official docs and .d.ts files in node_modules/. Never assume props, tokens, or APIs work without verification."

Source: Coding guidelines


1-38: LGTM!

Also applies to: 56-113

src/screenSharing/popoutPickerRequest.ts (1)

1-43: LGTM!

rollup.config.mjs (1)

270-303: LGTM!

src/screenSharing/main/resolveStandaloneOriginWindow.main.spec.ts (1)

1-90: LGTM!

src/screenSharing/screenPicker/types.ts (1)

1-2: LGTM!

Also applies to: 23-30

src/screenSharing/screenPicker/providers/InternalPickerProvider.ts (1)

1-2: LGTM!

Also applies to: 23-25, 34-37, 50-55

src/screenSharing/screenPicker/providers/PortalPickerProvider.ts (1)

13-14: LGTM!

- CSP: add default-src 'self' baseline (img-src data: for source
  thumbnails, style-src 'unsafe-inline' for injected styles)
- will-navigate: deny all navigation instead of allowing file://
…nt races

Electron's setDisplayMediaRequestHandler callback only accepts null as
a deny (DisplayMediaDeviceChosen in electron_browser_context.cc);
{video: false} and {} both throw a TypeError, surfacing as unhandled
rejections in main and leaving the renderer request hanging until
Chromium times out. Replace all 19 deny sites with cb(null), type
DisplayMediaCallback as nullable, and drop the as-any casts that hid
the invalid shape (boundary casts only where Electron's raw callback,
whose d.ts omits null, crosses into the typed interface).

Tracker settlement fixes from review verification:
- markComplete moved into finishActive: tracker no longer looks idle
  while desktopCapturer.getSources validation is in flight, so
  concurrent requests queue instead of draining the queue early or
  starting a second active request
- entry.settled re-checked after the await: cancellation during
  validation can no longer double-settle the callback
- cleanup() settles the active entry before clearing it

Adds regression tests for cancel-during-validation, queue-during-
validation, and cleanup mid-request.
Aligns the provider specs introduced on master (#3393) with this
branch's display-media changes: deny shape is callback(null) and the
internal picker handler now receives an optional originWindow.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts (1)

19-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test verifying originWindow is forwarded to the handler.

The current test only covers originWindow = undefined. Since threading originWindow through providers is a key change in this PR, a test with an actual BrowserWindow would verify the forwarding path.

✨ Suggested additional test
   it('forwards request to provided handler', () => {
     const callback = jest.fn() as jest.MockedFunction<DisplayMediaCallback>;
     const handle = jest.fn((cb: DisplayMediaCallback) => cb(null));

     provider.setHandleRequestHandler(handle);
     provider.handleDisplayMediaRequest(callback);

     expect(handle).toHaveBeenCalledWith(callback, undefined);
     expect(callback).toHaveBeenCalledWith(null);
   });
+
+  it('forwards originWindow to provided handler', () => {
+    const callback = jest.fn() as jest.MockedFunction<DisplayMediaCallback>;
+    const handle = jest.fn();
+    const mockWindow = {} as BrowserWindow;
+
+    provider.setHandleRequestHandler(handle);
+    provider.handleDisplayMediaRequest(callback, mockWindow);
+
+    expect(handle).toHaveBeenCalledWith(callback, mockWindow);
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`
around lines 19 - 28, Add a test alongside the existing “forwards request to
provided handler” case that creates or mocks a BrowserWindow, invokes
handleDisplayMediaRequest with that originWindow, and asserts the registered
handler receives both the callback and the same BrowserWindow instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`:
- Line 12: Rename the test case describing the missing handler in
InternalPickerProvider tests so it states that the callback is invoked with
null, matching the existing assertion and current behavior.

In `@src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts`:
- Line 22: Rename the tests in PortalPickerProvider.spec.ts at the cases around
“falls back to video false when no sources are selected” and the corresponding
second case so their descriptions state that video falls back to null, matching
the current assertions.

---

Nitpick comments:
In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`:
- Around line 19-28: Add a test alongside the existing “forwards request to
provided handler” case that creates or mocks a BrowserWindow, invokes
handleDisplayMediaRequest with that originWindow, and asserts the registered
handler receives both the callback and the same BrowserWindow instance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02b98d6f-1358-47f3-9202-068d756c61a1

📥 Commits

Reviewing files that changed from the base of the PR and between 47d0832 and 76048a2.

📒 Files selected for processing (11)
  • src/public/screen-picker-window.html
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/videoCallWindow/ipc.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/public/screen-picker-window.html
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/videoCallWindow/ipc.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
  • GitHub Check: CodeRabbit / Review
  • GitHub Check: check (macos-latest)
  • GitHub Check: check (windows-latest)
  • GitHub Check: check (ubuntu-latest)
  • GitHub Check: build (macos-latest, mac)
  • GitHub Check: build (ubuntu-latest, linux)
  • GitHub Check: build (windows-latest, windows)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript for all new code unless explicitly told otherwise.
Use Fuselage components for all UI work; only create custom components when Fuselage does not provide what is needed.
Import UI components from @rocket.chat/fuselage.
Check Theme.d.ts for valid color tokens before using Fuselage theme colors.
Use optional chaining with fallbacks for platform-specific APIs, especially Linux-only process APIs like process.getuid(), process.getgid(), process.geteuid(), and process.getegid().
Only mock platform-specific APIs when defensive coding is not possible.
Use TypeScript strict mode.
Use React functional components with hooks.
Redux actions must follow the Flux Standard Action (FSA) pattern.
Use camelCase for file names and PascalCase for components.

**/*.{ts,tsx}: Use TypeScript for new code unless explicitly told otherwise.
Use Fuselage components from @rocket.chat/fuselage for UI work unless the design requires something Fuselage does not provide.
Check Theme.d.ts for valid color tokens before using Fuselage colors.
Verify library props, APIs, and tokens against official docs or local .d.ts files instead of assuming.
Use React functional components with hooks.
Redux actions follow FSA shape.
Use camelCase for file names and PascalCase for components.
Prefer clear names over unnecessary comments.
Prefer editing existing files over creating new abstractions unless the new abstraction removes real complexity or matches an existing pattern.

Files:

  • src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.spec.ts for renderer process tests.

Files:

  • src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Renderer specs use *.spec.ts / *.spec.tsx.

Files:

  • src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
**/*.main.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.main.spec.ts for main process tests.

Main-process specs use *.main.spec.ts.

Files:

  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
src/*/*/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Renderer specs must live in a Jest-matched nested path, such as src/<module>/<subdir>/*.spec.ts(x); flat src/<module>/*.spec.ts files are not discovered by the current testMatch.

Files:

  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
🔇 Additional comments (7)
src/screenSharing/ScreenSharingRequestTracker.ts (1)

57-77: LGTM!

Also applies to: 103-103, 109-155, 164-189, 206-206, 251-251, 270-270

src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts (1)

151-151: LGTM!

Also applies to: 161-161, 185-185, 217-255, 273-273, 314-344, 380-380, 395-395, 410-410, 422-430, 471-471, 495-501, 532-532, 597-613, 625-625, 641-641

src/screenSharing/screenPickerWindow.ts (1)

60-62: LGTM!

src/screenSharing/screenPicker/providers/PortalPickerProvider.ts (1)

13-14: LGTM!

Also applies to: 31-39

src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts (1)

30-60: LGTM!

src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts (1)

31-41: LGTM!

Also applies to: 55-58

src/screenSharing/serverViewScreenSharing.ts (1)

117-133: LGTM!

Also applies to: 173-203

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts (1)

19-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test verifying originWindow is forwarded to the handler.

The current test only covers originWindow = undefined. Since threading originWindow through providers is a key change in this PR, a test with an actual BrowserWindow would verify the forwarding path.

✨ Suggested additional test
   it('forwards request to provided handler', () => {
     const callback = jest.fn() as jest.MockedFunction<DisplayMediaCallback>;
     const handle = jest.fn((cb: DisplayMediaCallback) => cb(null));

     provider.setHandleRequestHandler(handle);
     provider.handleDisplayMediaRequest(callback);

     expect(handle).toHaveBeenCalledWith(callback, undefined);
     expect(callback).toHaveBeenCalledWith(null);
   });
+
+  it('forwards originWindow to provided handler', () => {
+    const callback = jest.fn() as jest.MockedFunction<DisplayMediaCallback>;
+    const handle = jest.fn();
+    const mockWindow = {} as BrowserWindow;
+
+    provider.setHandleRequestHandler(handle);
+    provider.handleDisplayMediaRequest(callback, mockWindow);
+
+    expect(handle).toHaveBeenCalledWith(callback, mockWindow);
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`
around lines 19 - 28, Add a test alongside the existing “forwards request to
provided handler” case that creates or mocks a BrowserWindow, invokes
handleDisplayMediaRequest with that originWindow, and asserts the registered
handler receives both the callback and the same BrowserWindow instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`:
- Line 12: Rename the test case describing the missing handler in
InternalPickerProvider tests so it states that the callback is invoked with
null, matching the existing assertion and current behavior.

In `@src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts`:
- Line 22: Rename the tests in PortalPickerProvider.spec.ts at the cases around
“falls back to video false when no sources are selected” and the corresponding
second case so their descriptions state that video falls back to null, matching
the current assertions.

---

Nitpick comments:
In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts`:
- Around line 19-28: Add a test alongside the existing “forwards request to
provided handler” case that creates or mocks a BrowserWindow, invokes
handleDisplayMediaRequest with that originWindow, and asserts the registered
handler receives both the callback and the same BrowserWindow instance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02b98d6f-1358-47f3-9202-068d756c61a1

📥 Commits

Reviewing files that changed from the base of the PR and between 47d0832 and 76048a2.

📒 Files selected for processing (11)
  • src/public/screen-picker-window.html
  • src/screenSharing/ScreenSharingRequestTracker.ts
  • src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts
  • src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/screenSharing/screenPicker/providers/PortalPickerProvider.ts
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPickerWindow.ts
  • src/screenSharing/serverViewScreenSharing.ts
  • src/videoCallWindow/ipc.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/public/screen-picker-window.html
  • src/screenSharing/screenPicker/types.ts
  • src/screenSharing/screenPicker/providers/InternalPickerProvider.ts
  • src/videoCallWindow/ipc.ts
📜 Review details
🔇 Additional comments (7)
src/screenSharing/ScreenSharingRequestTracker.ts (1)

57-77: LGTM!

Also applies to: 103-103, 109-155, 164-189, 206-206, 251-251, 270-270

src/screenSharing/main/ScreenSharingRequestTracker.main.spec.ts (1)

151-151: LGTM!

Also applies to: 161-161, 185-185, 217-255, 273-273, 314-344, 380-380, 395-395, 410-410, 422-430, 471-471, 495-501, 532-532, 597-613, 625-625, 641-641

src/screenSharing/screenPickerWindow.ts (1)

60-62: LGTM!

src/screenSharing/screenPicker/providers/PortalPickerProvider.ts (1)

13-14: LGTM!

Also applies to: 31-39

src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts (1)

30-60: LGTM!

src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts (1)

31-41: LGTM!

Also applies to: 55-58

src/screenSharing/serverViewScreenSharing.ts (1)

117-133: LGTM!

Also applies to: 173-203

🛑 Comments failed to post (2)
src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts (1)

12-12: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale test name to match null assertion.

The test name says "invokes callback with false" but the assertion at line 16 checks for null. This is left over from the old callback({ video: false } as any) behavior.

✏️ Proposed fix
-  it('invokes callback with false when handler is missing', () => {
+  it('invokes callback with null when handler is missing', () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  it('invokes callback with null when handler is missing', () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/screenPicker/__tests__/InternalPickerProvider.spec.ts` at
line 12, Rename the test case describing the missing handler in
InternalPickerProvider tests so it states that the callback is invoked with
null, matching the existing assertion and current behavior.
src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts (1)

22-22: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale test names to match null assertions.

Both test names reference "false" but the assertions check for null, left over from the old callback({ video: false } as any) behavior.

✏️ Proposed fix
-  it('falls back to video false when no sources are selected', async () => {
+  it('calls callback with null when no sources are selected', async () => {
-  it('returns false on desktop capturer error', async () => {
+  it('calls callback with null on desktop capturer error', async () => {

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screenSharing/screenPicker/__tests__/PortalPickerProvider.spec.ts` at
line 22, Rename the tests in PortalPickerProvider.spec.ts at the cases around
“falls back to video false when no sources are selected” and the corresponding
second case so their descriptions state that video falls back to null, matching
the current assertions.

@jeanfbrito jeanfbrito merged commit 5597070 into master Jul 10, 2026
11 checks passed
@jeanfbrito jeanfbrito deleted the feat/SSGA-31-popout-screen-picker branch July 10, 2026 13:01
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.

1 participant