Skip to content

Core - Cancel pending tasks when browser crashed or v8 context gets released #5145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

campersau
Copy link
Contributor

@campersau campersau commented Jul 23, 2025

Fixes: #5144

Summary:

  • Pending tasks from EvaluateScriptAsync are now being canceled when the V8Context gets released / the browser crashes / the browser gets disposed.

Changes:

  • Storing pending tasks per frame (e.g. V8Context) instead of just globally, this makes it possible to cancel them

How Has This Been Tested?
Unittest

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Updated documentation

Checklist:

  • Tested the code(if applicable)
  • Commented my code
  • Changed the documentation(if applicable)
  • New files have a license disclaimer
  • The formatting is consistent with the project (project supports .editorconfig)

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for JavaScript evaluation when the browser or frame is unavailable, providing clearer exceptions for end-users.
    • Enhanced cancellation of pending JavaScript tasks when browser frames are lost or the browser is disposed, reducing the chance of hanging operations.
  • New Features

    • Added more granular management and cancellation of JavaScript tasks on a per-frame basis.
  • Tests

    • Introduced new tests to verify script evaluation and callback behavior during V8 context lifecycle changes, browser crashes, and repeated callback executions.
  • Refactor

    • Updated internal task management to group and handle pending tasks by frame, improving reliability and resource cleanup.
    • Streamlined task cancellation logic and optimized timeout handling for better performance.

Copy link

coderabbitai bot commented Jul 23, 2025

Walkthrough

The changes introduce frame-scoped management and cancellation of pending JavaScript tasks within the browser integration. Task repositories are refactored to group tasks by frame identifier, and new APIs for canceling tasks per frame or globally are added. Error handling is improved to return faulted tasks instead of returning null pointers. New tests validate context lifecycle and cancellation on browser crashes, including repeated callback execution.

Changes

File(s) Change Summary
CefSharp.Core.Runtime Internals
CefFrameWrapper.cpp, JavascriptCallbackProxy.cpp
Improved error handling in EvaluateScriptAsync to return faulted tasks; pending task creation now includes frame ID; logic consolidated inside frame validity check.
CefSharp.Core.Runtime Internals
ClientAdapter.cpp, ClientAdapter.h
Added cancellation of pending tasks per frame and globally at key lifecycle events; destructor updated to cancel tasks instead of deleting repository.
CefSharp/Internals
PendingTaskRepository.cs
Refactored to group pending tasks by frame ID with FramePendingTaskRepository; added APIs to cancel tasks per frame or globally; updated method signatures accordingly.
CefSharp/Internals
TaskExtensions.cs
Simplified timeout cancellation logic in WithTimeout method to rely on TrySetCanceled return value and removed redundant calls.
CefSharp.Test/Javascript
EvaluateScriptAsyncTests.cs, JavascriptCallbackTests.cs
Added tests covering V8 context lifecycle, cancellation on browser crash, repeated callback execution, and minor formatting and using directive additions.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test
    participant Browser as ChromiumWebBrowser
    participant Frame as CefFrameWrapper
    participant Repo as PendingTaskRepository
    participant Host as BrowserHost

    Test->>Browser: EvaluateScriptAsync(...)
    Browser->>Frame: EvaluateScriptAsync(...)
    alt Browser host available
        Frame->>Repo: CreatePendingTask(frameId, timeout)
        Repo-->>Frame: TaskCompletionSource
        Frame->>Host: Send script for evaluation
        Host-->>Frame: Script result
        Frame->>Repo: RemovePendingTask(frameId, taskId)
        Repo-->>Frame: Complete Task
    else Host not available
        Frame-->>Test: Return faulted Task (InvalidOperationException)
    end
Loading
sequenceDiagram
    participant Browser as ChromiumWebBrowser
    participant Repo as PendingTaskRepository

    Browser->>Repo: CancelPendingTasks(frameId)
    Repo->>Repo: Cancel all tasks for frameId
    Repo-->>Browser: Tasks canceled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~90 minutes

Poem

In burrows deep where frames reside,
The rabbits now can cancel, not just hide.
Tasks grouped by frame, with care they’re tracked,
Errors returned, no more abrupt crack!
With tests that bound from context to OOM,
The code hops forward—make some room!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 432cc2e and 774abb1.

📒 Files selected for processing (8)
  • CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (1 hunks)
  • CefSharp.Core.Runtime/Internals/ClientAdapter.cpp (3 hunks)
  • CefSharp.Core.Runtime/Internals/ClientAdapter.h (2 hunks)
  • CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (1 hunks)
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (3 hunks)
  • CefSharp.Test/Javascript/JavascriptCallbackTests.cs (3 hunks)
  • CefSharp/Internals/PendingTaskRepository.cs (2 hunks)
  • CefSharp/Internals/TaskExtensions.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • CefSharp.Core.Runtime/Internals/ClientAdapter.h
  • CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp
  • CefSharp/Internals/TaskExtensions.cs
  • CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs
  • CefSharp.Core.Runtime/Internals/ClientAdapter.cpp
🔇 Additional comments (12)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (4)

9-9: LGTM!

The import is necessary for accessing CefExample.HelloWorldUrl used in the new test methods.


27-77: Excellent comprehensive test for V8 context lifecycle.

This test thoroughly validates the new frame-scoped task cancellation behavior across multiple V8 context lifecycle scenarios:

  • Error handling before V8 context creation
  • Successful callback execution after context creation
  • Task cancellation on context changes
  • Task cancellation on browser disposal

The test structure with proper resource management and detailed assertions aligns perfectly with the PR objectives.


79-93: LGTM! Clean test for crash scenario.

This test effectively validates that JavaScript callbacks are properly canceled when the browser crashes, which directly addresses a key PR objective. The use of chrome://crash and assertion for TaskCanceledException is appropriate.


306-322: Good regression test for multiple executions.

This test ensures that the new frame-scoped task management doesn't break the existing functionality of executing JavaScript callbacks multiple times. The simple structure with repeated execution validation is appropriate.

CefSharp/Internals/PendingTaskRepository.cs (8)

22-23: Excellent architectural improvement for frame-scoped task management.

The refactoring from global dictionaries to frame-scoped repositories enables precise task cancellation when V8 contexts are released or browsers crash, which directly addresses the PR objectives.


30-60: Well-implemented frame-scoped task creation.

The method correctly:

  • Requires frameId parameter for frame-scoped management
  • Uses conditional compilation to leverage newer .NET overloads where available
  • Maintains thread-safety with AddOrUpdate
  • Properly integrates frameId into timeout handling

The implementation supports the core PR functionality effectively.


65-95: Consistent implementation for callback task creation.

The method mirrors the pattern used in CreatePendingTask with proper frame-scoped management for JavaScript callback tasks. The conditional compilation and thread-safety considerations are correctly applied.


101-120: Safe implementation of frame-scoped task removal.

The method correctly handles the two-level lookup (frame then task) with appropriate null checks. The defensive programming approach ensures robustness when frames or tasks don't exist.


126-145: Consistent callback task removal implementation.

The method follows the same safe pattern as RemovePendingTask while correctly operating on the callback task dictionary. The implementation maintains proper task type separation within frames.


147-158: Essential method for frame-specific task cancellation.

This new method directly enables the core PR functionality by allowing cancellation of all tasks within a specific frame when the V8 context is released. The atomic TryRemove followed by Dispose() ensures proper cleanup.


160-170: Important method for global task cancellation.

This method enables cancellation of all pending tasks across all frames, which is essential for browser crash scenarios and global cleanup. The implementation safely disposes all repositories before clearing the main dictionary.


172-193: Well-designed frame repository with proper cleanup.

The nested class effectively encapsulates frame-specific task storage and implements safe cancellation using TrySetCanceled() as recommended in previous reviews. The Dispose() method properly handles both task types and clears dictionaries.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 1

🧹 Nitpick comments (1)
CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (1)

68-83: Consider reducing the loop sizes to make the test more practical.

While the test correctly validates OOM cancellation behavior, the nested loops with 10M x 10M iterations creating 100M character strings could cause test instability or excessive runtime. Consider smaller values that still reliably trigger OOM.

-                let array1 = [];
-                for (let i = 0; i < 10000000; i++) {
-                    let array2 = [];
-                    for (let j = 0; j < 10000000; j++) {
-                        array2.push('a'.repeat(100000000));
-                    }
-                    array1.push(array2);
-                }
+                let array1 = [];
+                for (let i = 0; i < 1000; i++) {
+                    let array2 = [];
+                    for (let j = 0; j < 1000; j++) {
+                        array2.push('a'.repeat(10000000));
+                    }
+                    array1.push(array2);
+                }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cde57b6 and 7adac4c.

📒 Files selected for processing (7)
  • CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (1 hunks)
  • CefSharp.Core.Runtime/Internals/ClientAdapter.cpp (3 hunks)
  • CefSharp.Core.Runtime/Internals/ClientAdapter.h (2 hunks)
  • CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (1 hunks)
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (3 hunks)
  • CefSharp/Internals/PendingTaskRepository.cs (2 hunks)
  • CefSharp/Internals/TaskExtensions.cs (1 hunks)
🧠 Learnings (1)
CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (1)

Learnt from: amaitland
PR: #4475
File: CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp:147-154
Timestamp: 2025-05-10T23:58:24.668Z
Learning: The GetCache method of the IJavaScriptObjectCache implementation should never return null in its current implementation. Both the legacy and per-browser cache implementations guarantee a non-null return value, with the per-browser implementation returning an empty dictionary when no cache exists for a given browser ID.

🧬 Code Graph Analysis (3)
CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (3)
CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h (1)
  • CefBrowserWrapper (27-75)
CefSharp.Core.Runtime/Internals/StringUtils.h (1)
  • ToNative (74-84)
CefSharp.Core.Runtime/Internals/Serialization/Primitives.cpp (2)
  • SetInt64 (41-49)
  • SetInt64 (41-41)
CefSharp.Core.Runtime/Internals/ClientAdapter.cpp (2)
CefSharp.Core.Runtime/Internals/CefRequestContextHandlerAdapter.cpp (1)
  • ToClr (52-52)
CefSharp.Core.Runtime/Internals/Serialization/Primitives.cpp (2)
  • GetInt64 (52-60)
  • GetInt64 (52-52)
CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (3)
CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h (1)
  • Identifier (109-112)
CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h (1)
  • Identifier (117-120)
CefSharp.Core.Runtime/Request.h (1)
  • Identifier (69-69)
🧰 Additional context used
🧠 Learnings (1)
CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (1)

Learnt from: amaitland
PR: #4475
File: CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp:147-154
Timestamp: 2025-05-10T23:58:24.668Z
Learning: The GetCache method of the IJavaScriptObjectCache implementation should never return null in its current implementation. Both the legacy and per-browser cache implementations guarantee a non-null return value, with the per-browser implementation returning an empty dictionary when no cache exists for a given browser ID.

🧬 Code Graph Analysis (3)
CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (3)
CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h (1)
  • CefBrowserWrapper (27-75)
CefSharp.Core.Runtime/Internals/StringUtils.h (1)
  • ToNative (74-84)
CefSharp.Core.Runtime/Internals/Serialization/Primitives.cpp (2)
  • SetInt64 (41-49)
  • SetInt64 (41-41)
CefSharp.Core.Runtime/Internals/ClientAdapter.cpp (2)
CefSharp.Core.Runtime/Internals/CefRequestContextHandlerAdapter.cpp (1)
  • ToClr (52-52)
CefSharp.Core.Runtime/Internals/Serialization/Primitives.cpp (2)
  • GetInt64 (52-60)
  • GetInt64 (52-52)
CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (3)
CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h (1)
  • Identifier (109-112)
CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h (1)
  • Identifier (117-120)
CefSharp.Core.Runtime/Request.h (1)
  • Identifier (69-69)
🔇 Additional comments (20)
CefSharp.Core.Runtime/Internals/ClientAdapter.h (2)

73-73: Good change to cancel tasks before cleanup.

The shift from direct deletion to explicit task cancellation aligns with the PR objective of properly canceling pending JavaScript tasks when the V8 context is released or the browser crashes.


82-82: Appropriate nullification after task cancellation.

Setting the pointer to nullptr after canceling tasks is consistent with the new lifecycle management approach.

CefSharp/Internals/TaskExtensions.cs (1)

23-23: Good simplification of cancellation logic.

Relying directly on TrySetCanceled() return value is cleaner and more robust than the previous status check, as the method already handles the completed task scenario internally.

CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp (2)

240-240: Improved error handling with faulted Task.

Returning a faulted Task instead of nullptr provides better error propagation and consistent API behavior when the browser host is unavailable.


248-248: Frame-scoped task management implementation.

Adding the Identifier parameter enables frame-scoped task management, which is essential for the PR objective of canceling pending tasks when V8 contexts are released.

CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (2)

30-30: Consistent error handling pattern.

Returning a faulted Task instead of throwing an exception maintains consistency with the error handling pattern established in CefFrameWrapper::EvaluateScriptAsync.


39-54: Frame-scoped task management and improved logic flow.

Moving the callback task creation and message construction inside the frame validity check ensures tasks are only created for valid frames, and the addition of _callback->FrameId parameter enables proper frame-scoped task tracking.

CefSharp.Core.Runtime/Internals/ClientAdapter.cpp (4)

704-704: Essential cleanup on render process termination.

Canceling all pending tasks when the render process terminates ensures proper cleanup and prevents dangling tasks, which is a core objective of this PR.


387-389: Frame-scoped task cancellation on context release.

Extracting the frame ID and canceling frame-specific pending tasks when the V8 context is released is exactly what the PR aims to achieve - proper cleanup when contexts become invalid.


393-393: Consistent use of extracted frame ID.

Using the pre-extracted frameId variable maintains consistency and avoids redundant calls to StringUtils::ToClr(frame->GetIdentifier()).


484-484: Frame-aware task removal operations.

Adding the frameId parameter to both RemovePendingTask and RemoveJavascriptCallbackPendingTask calls enables proper frame-scoped task management, ensuring tasks are tracked and removed at the correct granularity.

Also applies to: 492-493

CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (3)

11-11: LGTM!

The using statement is appropriately added to support the new test methods.


30-66: Well-structured test for V8 context lifecycle!

The test comprehensively validates the frame-scoped task cancellation behavior across different V8 context states, which aligns perfectly with the PR objectives.


323-323: Good formatting consistency fix.

CefSharp/Internals/PendingTaskRepository.cs (6)

22-23: Good refactoring to support frame-scoped task management.

The new structure properly encapsulates tasks by frame ID, which aligns with the PR objective of canceling tasks when V8 context is released.


33-60: Correctly implements frame-scoped task creation.

The method properly handles both creating new frame repositories and adding tasks to existing ones, with appropriate framework-specific implementations.


69-94: Consistent implementation for callback task creation.

The method correctly maintains the separation between regular and callback tasks while implementing frame-scoped management.


107-120: Proper implementation of frame-scoped task removal.


147-158: Key implementation for frame-specific task cancellation.

This method enables the core PR objective of canceling all pending tasks when a V8 context is released or frame is destroyed.


160-170: Comprehensive global task cancellation.

Properly handles cancellation of all pending tasks across all frames, which is essential for browser disposal scenarios.

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

Copy link

@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: 0

🧹 Nitpick comments (2)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (2)

27-71: Comprehensive test coverage for V8 context lifecycle management.

This test effectively validates the key scenarios outlined in the PR objectives:

  • Script evaluation failure before V8 context creation
  • Successful callback execution after context availability
  • Task cancellation on context changes and browser disposal

The test logic is sound and the resource management with using statements is appropriate.

Consider these improvements for test robustness:

  1. Timing dependency: The 1000ms timeout in promises could cause flakiness in CI environments. Consider using a shorter timeout or mocking time-sensitive operations.

  2. Exception validation: Line 36 only checks the message prefix. Consider validating the specific exception type for better test precision:

-var withoutV8ContextException = await Assert.ThrowsAsync<Exception>(() => browser.EvaluateScriptAsync("(function() { return 1+1; })"));
+var withoutV8ContextException = await Assert.ThrowsAsync<InvalidOperationException>(() => browser.EvaluateScriptAsync("(function() { return 1+1; })"));

73-87: Excellent focused test for crash scenario validation.

This test cleanly validates the critical crash scenario outlined in the PR objectives. The use of chrome://crash is the standard approach for simulating browser crashes in Chromium-based testing, and the assertion logic correctly expects TaskCanceledException.

The test structure is well-organized and focused on a single concern.

Minor consideration for test reliability:

Similar to the previous test, the 1000ms timeout could potentially cause timing issues in slower CI environments, though it's likely sufficient for crash testing scenarios.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1389aff and f1567eb.

📒 Files selected for processing (2)
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (3 hunks)
  • CefSharp.Test/Javascript/JavascriptCallbackTests.cs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs
🔇 Additional comments (1)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1)

9-9: LGTM: Clean namespace addition for test support.

The using statement is appropriately added to support the CefExample.HelloWorldUrl usage in the new test methods.

@AppVeyorBot
Copy link

Copy link

@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: 0

🧹 Nitpick comments (1)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1)

27-77: Comprehensive test coverage for V8 context lifecycle scenarios.

This test method effectively validates the core functionality described in the PR objectives - canceling pending tasks when V8 context is released or browser is disposed. The test structure is well-organized and covers multiple scenarios:

  1. ✅ Script evaluation failure before V8 context creation
  2. ✅ Successful callback execution after context creation
  3. ✅ Task cancellation on V8 context change (reload)
  4. ✅ Task cancellation on browser disposal

The async/await patterns and exception assertions are correctly implemented.

However, consider these minor improvements:

-                var callbackExecuteWithoutV8ContextException = await Assert.ThrowsAsync<Exception>(() => browser.EvaluateScriptAsync("(function() { return 1+1; })"));
+                var withoutContextException = await Assert.ThrowsAsync<Exception>(() => browser.EvaluateScriptAsync("(function() { return 1+1; })"));
-                Assert.StartsWith("Unable to execute javascript at this time", withoutV8ContextException.Message);
+                Assert.StartsWith("Unable to execute javascript at this time", withoutContextException.Message);

The variable names could be more concise while maintaining clarity.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1567eb and 432cc2e.

📒 Files selected for processing (3)
  • CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp (1 hunks)
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs (3 hunks)
  • CefSharp.Test/Javascript/JavascriptCallbackTests.cs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp
  • CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs
🔇 Additional comments (3)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (3)

9-9: LGTM: Import addition is appropriate.

The CefSharp.Example import is correctly added and used in the test methods to access CefExample.HelloWorldUrl.


79-93: Excellent test for browser crash scenario.

This test method directly validates one of the core PR objectives - ensuring pending tasks are canceled when the browser crashes. The implementation is clean and effective:

  • Uses chrome://crash to simulate browser crash condition
  • Properly tests that delayed promise callbacks are canceled
  • Correctly expects TaskCanceledException

The test aligns perfectly with the PR's goal of handling task cancellation during browser crashes.


306-322: Good test coverage for repeated callback execution.

This test method ensures that the refactored frame-scoped task management system maintains compatibility with existing callback behavior. The test validates that:

  • Callbacks can be executed multiple times successfully
  • Results remain consistent across executions
  • No issues arise from the new task repository structure

The simple but effective test design provides confidence in the backwards compatibility of the changes.

@AppVeyorBot
Copy link

@amaitland
Copy link
Member

Thanks for the PR 👍

Any ideas what's causing the tests to fail when run on appveyor?

@campersau
Copy link
Contributor Author

I currently don't know, my run did work: https://ci.appveyor.com/project/campersau/cefsharp/builds/52449336
I will try to trigger CI again.

@AppVeyorBot
Copy link

@campersau
Copy link
Contributor Author

It looks like the same issue happens in main from time to time as well. E.g.: 633a8cd
https://ci.appveyor.com/project/cefsharp/cefsharp/builds/52467909

@amaitland
Copy link
Member

The GPU crash happens semi regularly, hopefully switching to SwANGLE will help there.

The DevTools test failing is fairly new, likely unrelated to this PR.

[3680:6244:0723/075108.525:ERROR:cef\libcef\browser\browser_info_manager.cc:841] Timeout of new browser info response for frame 10D-E8D5D5D252403C189AEB339E7CB4A081 (has_rfh=1)

I don't remember seeing this before, looking at a few other failed ones and it has happened prior, so maybe unrelated.

@amaitland
Copy link
Member

I'd asked about the GCM errors at https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=20318

Might have to follow up to see if there's anything further related to those.

@amaitland amaitland force-pushed the cancelpendingtasksoncrashmaster branch from a942fc5 to 774abb1 Compare August 1, 2025 09:41
@amaitland
Copy link
Member

I should be able to look at this over the weekend.

@AppVeyorBot
Copy link

Copy link
Member

@amaitland amaitland left a comment

Choose a reason for hiding this comment

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

Overall looks excellent! Thanks

Couple of minor things to discuss inline.

framePendingTasks.Clear();
}

private sealed class FramePendingTaskRepository : IDisposable
Copy link
Member

Choose a reason for hiding this comment

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

Class in own file please.

Probably worth generating a few simple test cases for this class

@@ -70,8 +70,7 @@ namespace CefSharp

CloseAllPopups(true);

//this will dispose the repository and cancel all pending tasks
delete _pendingTaskRepository;
Copy link
Member

Choose a reason for hiding this comment

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

delete will call Dispose if it's implemented. Guess it's semantics, might be worth having pending task repository implement IDisposable.

@@ -23,6 +24,74 @@ public JavascriptCallbackTests(ITestOutputHelper output, CefSharpFixture collect
this.collectionFixture = collectionFixture;
}

[Fact]
public async Task V8Context()
Copy link
Member

Choose a reason for hiding this comment

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

Thoughts on a more descriptive name for this test?

@@ -26,6 +27,52 @@ public EvaluateScriptAsyncTests(ITestOutputHelper output, CefSharpFixture collec
this.collectionFixture = collectionFixture;
}

[Fact]
public async Task V8Context()
Copy link
Member

Choose a reason for hiding this comment

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

Thoughts on a more descriptive name for this test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants