Skip to content

Clean afp links master#3253

Open
Ram-sah19 wants to merge 2 commits intoRocketChat:masterfrom
Ram-sah19:clean-afp-links-master
Open

Clean afp links master#3253
Ram-sah19 wants to merge 2 commits intoRocketChat:masterfrom
Ram-sah19:clean-afp-links-master

Conversation

@Ram-sah19
Copy link

@Ram-sah19 Ram-sah19 commented Mar 11, 2026

Adds taskbar icon blinking when a new message notification is received.

This improves visibility of unread messages when the application window is minimized or not focused.

Implementation:

  • Uses the existing notification system
  • Triggers attention drawing on new messages
  • Stops blinking once the notification is cleared

Summary by CodeRabbit

  • New Features

    • External URLs are now validated for security before opening in your browser.
  • Bug Fixes

    • Improved notification attention feedback consistency for text and voice notification types.
    • Notification dismissal now properly clears attention effects across all notifications.
    • App now automatically recovers from crashes by clearing data and reloading.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

Walkthrough

Adds external URL handling with IPC-based validation and protocol checking, modifies notification attention behavior to apply across notification types, and introduces a crash handler for renderer process recovery with session state clearing.

Changes

Cohort / File(s) Summary
External URL Handling
src/main.ts
Adds IPC listener for external URLs that validates format, checks allowed protocols via dynamic import, and opens URLs through appropriate shell or electron methods.
Notification Behavior
src/notifications/main.ts
Modifies notification attention flashing to apply to text or voice types and ensures attention is stopped unconditionally on dismissal/close events.
Crash Recovery
src/ui/main/rootWindow.ts
Registers one-time renderer crash handler that clears session storage and reloads the window on process failure.

Sequence Diagram(s)

sequenceDiagram
    participant IPC as IPC Main
    participant Handler as open-external Handler
    participant Nav as navigation/main
    participant Shell as shell/electron
    participant Window as BrowserWindow

    IPC->>Handler: 'open-external' event
    Handler->>Handler: Validate URL format
    Handler->>Nav: Import isProtocolAllowed
    Nav-->>Handler: Protocol validation function
    Handler->>Handler: Check if protocol allowed
    alt Protocol Allowed
        Handler->>Shell: openExternal() or shell.openExternal()
        Shell-->>Handler: URL opened
    else Protocol Blocked
        Handler->>Handler: Block disallowed protocol
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

type: feature

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Clean afp links master' is vague and does not clearly convey the main changes. The PR actually implements taskbar icon blinking for notifications and adds afp:// link support, but the title suggests only cleaning/organizing afp links, which is misleading about the scope of changes. Use a more descriptive title that captures the main changes, such as 'Add taskbar icon blinking for notifications and support afp:// links' or split into separate PRs for each feature.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/notifications/main.ts`:
- Around line 88-92: The predicate using notificationTypes.get(id) is always
true because options.notificationType is constrained to 'text'|'voice', so
change the blink decision to check an actual chat indicator instead: when
creating a notification (where options.notificationType is set) also store a
boolean or explicit source (e.g., notificationIsChat.set(id, true) or
notificationSource.set(id, 'chat')) and then here replace the current check
(notificationType === 'text' || notificationType === 'voice') with that
chat-specific check (e.g., notificationIsChat.get(id) === true or
notificationSource.get(id) === 'chat') before calling
attentionDrawing.drawAttention(id); ensure the value is written at creation so
the lookup is meaningful.

In `@src/ui/main/rootWindow.ts`:
- Around line 524-544: The handler attached to
browserWindow.webContents.on('render-process-gone', async (_event, details) => {
... }) indiscriminately clears the app's shared session via session.clearCache()
and session.clearStorageData(...), which wipes global cookies and persisted
state; change this to first inspect details.reason and only perform
cache/storage clearing for real crash/oom/abnormal conditions (e.g., 'crashed',
'oom', 'abnormal-exit', 'launch-failed') while skipping or logging for benign
reasons ('clean-exit', 'killed', 'memory-eviction', 'integrity-failure');
additionally, avoid clearing auth-bearing storages (cookies, serviceworkers,
cachestorage) unless you detect a crash loop or the BrowserWindow uses a
non-default partition—modify the logic around the session.clearCache and
session.clearStorageData calls so they run conditionally and target only safe
storages when necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bce09a87-fee8-4657-bb6c-eafd8e86c52d

📥 Commits

Reviewing files that changed from the base of the PR and between 49261f5 and 655fd55.

📒 Files selected for processing (3)
  • src/main.ts
  • src/notifications/main.ts
  • src/ui/main/rootWindow.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript strict mode enabled in TypeScript configuration
Use React functional components with hooks instead of class components
Follow FSA (Flux Standard Action) pattern for Redux actions
Use camelCase for file names and PascalCase for component file names
All code must pass ESLint and TypeScript checks
Write self-documenting code with clear naming; avoid unnecessary comments except for complex business logic or non-obvious decisions
Use Fuselage components from @rocket.chat/fuselage for all UI work and only create custom components when Fuselage doesn't provide what's needed
Check Theme.d.ts for valid color tokens when using Fuselage components
Use defensive coding with optional chaining and fallbacks for Linux-only APIs (process.getuid(), process.getgid(), process.geteuid(), process.getegid()) to ensure cross-platform compatibility across Windows, macOS, and Linux

Files:

  • src/ui/main/rootWindow.ts
  • src/main.ts
  • src/notifications/main.ts
🔇 Additional comments (1)
src/notifications/main.ts (1)

104-105: Safe simplification.

src/notifications/attentionDrawing.ts:46-76 already no-ops when an id was never tracked, so making stopAttention() unconditional on close/dismiss removes duplicate branching without changing behavior.

Also applies to: 250-251

Comment on lines +148 to +171
handle('open-external', async (_webContents, rawUrl) => {
let url: URL;

try {
url = new URL(rawUrl);
} catch {
console.warn('Blocked malformed external URL');
return;
}

const { isProtocolAllowed } = await import('./navigation/main');

if (!(await isProtocolAllowed(url.toString()))) {
console.warn('Blocked external URL with disallowed protocol');
return;
}

if (url.protocol === 'http:' || url.protocol === 'https:') {
await openExternal(url.toString());
return;
}

await shell.openExternal(url.toString());
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the handle function implementation in src/ipc/main.ts
fd "ipc.*main" --type f | head -20

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 57


🏁 Script executed:

# Read the main handler at lines 148-171
sed -n '140,180p' src/main.ts | cat -n

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 1370


🏁 Script executed:

# Read the handle function in src/ipc/main.ts around lines 29-43
fd "ipc" --type d

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 83


🏁 Script executed:

# Search for ipc/main.ts or similar
find . -path "*/ipc/main*" -type f

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 92


🏁 Script executed:

# Read the handle function in src/ipc/main.ts around lines 29-43
sed -n '20,50p' src/ipc/main.ts | cat -n

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 731


🏁 Script executed:

# Read the isProtocolAllowed function in src/navigation/main.ts around lines 213-241
sed -n '205,245p' src/navigation/main.ts | cat -n

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 1236


Lock this IPC handler to trusted frames and explicit schemes.

The _webContents parameter is unused, so this privileged endpoint cannot distinguish the app's main frame from an embedded or compromised frame. The handle() wrapper in src/ipc/main.ts passes event.sender directly without validating caller origin or frame source. isProtocolAllowed() in src/navigation/main.ts also allows any previously-persisted custom protocol via the Redux externalProtocols store, without restriction. This means any external protocol the user previously approved can be reopened by any frame. Before calling shell.openExternal(), validate the sender's origin against trusted app frames and explicitly allow only the schemes this app supports.

Comment on lines 88 to 92
const notificationType = notificationTypes.get(id);
if (notificationType === 'voice') {

// Only flash for chat-related notifications
if (notificationType === 'text' || notificationType === 'voice') {
attentionDrawing.drawAttention(id);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This predicate never filters anything.

Line 226 stores options.notificationType || 'text' for every new notification, and src/notifications/common.ts:1-26 constrains that value to 'text' | 'voice'. That makes the new notificationType === 'text' || notificationType === 'voice' check always true for created notifications, so this change does not actually limit blinking to message notifications.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/notifications/main.ts` around lines 88 - 92, The predicate using
notificationTypes.get(id) is always true because options.notificationType is
constrained to 'text'|'voice', so change the blink decision to check an actual
chat indicator instead: when creating a notification (where
options.notificationType is set) also store a boolean or explicit source (e.g.,
notificationIsChat.set(id, true) or notificationSource.set(id, 'chat')) and then
here replace the current check (notificationType === 'text' || notificationType
=== 'voice') with that chat-specific check (e.g., notificationIsChat.get(id) ===
true or notificationSource.get(id) === 'chat') before calling
attentionDrawing.drawAttention(id); ensure the value is written at creation so
the lookup is meaningful.

Comment on lines +524 to +544
browserWindow.webContents.on(
'render-process-gone',
async (_event, details) => {
console.error('Renderer process crashed:', details.reason);
try {
const { session } = browserWindow.webContents;
await session.clearCache();
await session.clearStorageData({
storages: [
'cookies',
'indexdb',
'filesystem',
'shadercache',
'websql',
'serviceworkers',
'cachestorage',
],
});
console.log('Cache cleared. Reloading window...');
browserWindow.reload();
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the file structure and the specific lines mentioned
find . -name "rootWindow.ts" -type f

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 122


🏁 Script executed:

# Once we find it, let's examine the code around lines 524-544
head -n 550 src/ui/main/rootWindow.ts | tail -n 50

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 1334


🏁 Script executed:

# Let's also check for the crashHandlerRegistered flag mentioned in the scratchpad
rg "crashHandlerRegistered" src/ui/main/rootWindow.ts -B 2 -A 2

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 377


🏁 Script executed:

# Check webPreferences to see if a custom partition is specified
rg "webPreferences|partition" src/ui/main/rootWindow.ts -B 3 -A 3

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 663


🌐 Web query:

Electron render-process-gone event reasons documentation

💡 Result:

Electron documents the 'render-process-gone' event’s details.reason values in the RenderProcessGoneDetails structure. The possible reason strings are: clean-exit, abnormal-exit, killed, crashed, oom, launch-failed, integrity-failure, and memory-eviction, with exitCode giving the process exit code (or a platform-specific launch failure code when reason is launch-failed). [1]

You’ll encounter this details object when handling 'render-process-gone' on webContents (and similarly on app / <webview>), which replaced older crash-related events. [2], [3]

Sources
[1] Electron docs — RenderProcessGoneDetails structure: https://www.electronjs.org/docs/latest/api/structures/render-process-gone-details
[2] Electron docs — webContents API (render-process-gone context): https://www.electronjs.org/docs/latest/api/web-contents
[3] Electron docs — Breaking changes (migration to render-process-gone): https://www.electronjs.org/docs/latest/breaking-changes


🌐 Web query:

Electron session.clearStorageData cookies indexdb impact shared session

💡 Result:

session.clearStorageData() clears Chromium storage inside a specific Electron Session (i.e., a storage partition)—optionally limited to a specific origin and/or specific storage types like cookies and indexdb. [1]

What it clears (relevant to your question)

  • If you include storages: ['cookies'], it deletes cookies for that session (and for the specified origin if you pass one). [1]
  • If you include storages: ['indexdb'], it deletes IndexedDB data for that session (and origin, if provided). [1]
  • If you omit storages, it clears all listed storage types for that session. [1]

Impact when the session is “shared”

Electron sessions are shared by partition:

  • session.fromPartition(partition) returns the same Session object for the same partition string. [1]
  • If the partition starts with persist:, it’s a persistent session available to all pages in the app using that same partition; without persist: it’s in-memory. [1][2]

So, calling clearStorageData() on a shared/persistent partition will affect every BrowserWindow/webContents using that same partition (they’ll all see cookies/IndexedDB disappear), but it won’t affect other partitions (including other persist:* partitions) or other Electron apps. [1][2]

Sources:
[1] Electron session API docs (clearStorageData, fromPartition, persist partitions) (electronjs.org)
[2] Stack Overflow discussion of persist: partitions being shared/persistent (stackoverflow.com)

Citations:


🏁 Script executed:

# Let's check if there's any filtering on details.reason in the handler
rg "details.reason" src/ui/main/rootWindow.ts -B 3 -A 3

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 349


🏁 Script executed:

# Check if there are any other handlers or guards for this event
rg "render-process-gone" src/ -B 2 -A 5

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 558


Don't erase the shared session on all renderer process terminations.

Lines 529–541 clear the main window's shared session without filtering on the termination reason. Electron's render-process-gone event reports diverse reasons—clean-exit, abnormal-exit, killed, crashed, oom, launch-failed, integrity-failure, and memory-eviction—but this handler treats all equally, immediately clearing storage including cookies, indexdb, serviceworkers, and cachestorage. Since no custom partition is specified in webPreferences, this clears the app's default shared session, which logs out users and erases persisted state app-wide on legitimate non-crash scenarios like clean-exit, killed, or memory-eviction. Gate recovery logic to actual crash reasons (crashed, oom, abnormal-exit, launch-failed) and avoid clearing auth-bearing storage unless you've detected a repeat crash loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/main/rootWindow.ts` around lines 524 - 544, The handler attached to
browserWindow.webContents.on('render-process-gone', async (_event, details) => {
... }) indiscriminately clears the app's shared session via session.clearCache()
and session.clearStorageData(...), which wipes global cookies and persisted
state; change this to first inspect details.reason and only perform
cache/storage clearing for real crash/oom/abnormal conditions (e.g., 'crashed',
'oom', 'abnormal-exit', 'launch-failed') while skipping or logging for benign
reasons ('clean-exit', 'killed', 'memory-eviction', 'integrity-failure');
additionally, avoid clearing auth-bearing storages (cookies, serviceworkers,
cachestorage) unless you detect a crash loop or the BrowserWindow uses a
non-default partition—modify the logic around the session.clearCache and
session.clearStorageData calls so they run conditionally and target only safe
storages when necessary.

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