Skip to content

Conversation

@amaitland
Copy link
Member

@amaitland amaitland commented Nov 30, 2025

Fixes:

Related to #4606

Summary:

On OnBrowserDestroyed is no longer reliable, gets called when it shouldn't

Forum thread https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=20428

Changes:

  • Restructure to store the JavascriptRootObjectWrapper per frame directly, instead of per browser, per frame

How Has This Been Tested?

TBA

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)
  • The formatting is consistent with the project (project supports .editorconfig)

Summary by CodeRabbit

  • Refactor

    • Centralized JavaScript root-object management so frame-scoped bindings use a single provided root-object wrapper.
  • Behavior Changes

    • Callback registry now derives context/browser identifiers at runtime instead of using a stored browser id; related constructors adjusted.
  • Chore

    • Removed per-browser storage of root-object wrappers and introduced a centralized frame-id mapping with lifecycle cleanup.
  • Tests

    • Updated tests to assert frame-aware messages and perform explicit navigation checks before script evaluation.

✏️ Tip: You can customize this high-level summary in your review settings.

…d of per browser

- Don't store JavascriptRootObjectWrapper in the browserWrapper as CEF is calling destroyed more than it should.
-
@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Walkthrough

Refactors JavaScript root object management from per-browser collections to a centralized frame-id keyed map, injects JavascriptRootObjectWrapper^ into binding handlers instead of a browser wrapper, removes browserId constructor parameters for callback/root-object types, and updates related tests for frame-aware behavior.

Changes

Cohort / File(s) Summary
BindObjectAsyncHandler update
CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h
Constructor now accepts JavascriptRootObjectWrapper^ (replacing CefBrowserWrapper^); field swapped to _javascriptRootObjectWrapper; null-checks, error messages, and binding logic updated to use the provided root object directly; per-frame wrapper creation removed.
Frame-scoped root-object mapping (header)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Added gcroot<ConcurrentDictionary<String^, JavascriptRootObjectWrapper^>^> _jsRootObjectWrappersByFrameId; initialized in constructor; destructor now cleans up the map and its contents.
Frame-scoped root-object mapping (impl)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Reworked lifecycle and messaging code to use _jsRootObjectWrappersByFrameId (GetJsRootObjectWrapper, OnContextCreated/Released, OnProcessMessageReceived, BindObjectAsync paths); replaced per-browser wrapper accesses with frame-id map lookups and creation logic (NETCOREAPP vs non-NETCOREAPP branches); simplified/error-path adjustments.
Removed per-browser root collection
CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h
Removed JavascriptRootObjectWrappers property, its initialization, and destructor cleanup from CefBrowserWrapper.
Callback registry decoupling
CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h, CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp
Removed _browserId field and constructor parameter; constructor is parameterless; Register now derives BrowserId at runtime from the current V8 context's browser identifier.
JavascriptRootObjectWrapper constructors
CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h
Constructor signatures changed: NETCOREAPP builds use a parameterless constructor; non-NETCOREAPP builds take IBrowserProcess^ (browserId removed); callback registry initialization no longer requires browserId.
Test updates
CefSharp.Test/Javascript/JavascriptCallbackTests.cs, CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs
Tests updated to reference frame identifiers in assertions and to perform explicit navigation via Browser.LoadUrlAsync with loadResponse.Success checks prior to script evaluation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • CefAppUnmanagedWrapper.cpp/.h: concurrency, lifecycle, and safe cleanup of _jsRootObjectWrappersByFrameId; NETCOREAPP vs non-NETCOREAPP creation differences.
    • Callers and ownership for injected JavascriptRootObjectWrapper^ (e.g., BindObjectAsyncHandler construction sites).
    • All call sites formerly using CefBrowserWrapper::JavascriptRootObjectWrappers to ensure replacements to the centralized map are complete.
    • JavascriptCallbackRegistry::Register: verify runtime-derived BrowserId preserves intended behavior across contexts.
    • Updated tests: confirm assertions and navigation changes are consistent with new frame-scoped model.

Suggested labels

breaking-change

Poem

I nibbled through the tangled shrubs of code,
I moved the roots to frames where they now bode.
No browser nests, just one neat map to keep,
I hop away — the bindings sleep. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main architectural change: rewriting the RenderProcess to avoid reliance on OnBrowserDestroyed, which is the core objective of the PR.
Description check ✅ Passed The description follows the template structure with Fixes, Summary, Changes, and Types of changes sections completed. However, 'How Has This Been Tested?' is marked as 'TBA' and testing-related checklist items remain unchecked.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)

❌ Error creating Unit Test PR.

  • Create PR with unit tests
  • Commit unit tests in branch rewrite/browserdestroyed
  • Post copyable unit tests in a comment

📜 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 c052def and 1e21f47.

📒 Files selected for processing (1)
  • CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (2)
CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1)
  • JavascriptRootObjectWrapper (31-100)
CefSharp.BrowserSubprocess.Core/Serialization/V8Serialization.cpp (9)
  • SerializeV8Object (24-120)
  • SerializeV8Object (24-24)
  • SerializeV8Object (123-134)
  • SerializeV8Object (123-123)
  • SerializeV8Object (198-198)
  • SerializeV8Object (199-199)
  • SerializeV8Object (200-200)
  • SerializeV8Object (201-201)
  • SerializeV8Object (202-202)
🔇 Additional comments (4)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (4)

223-236: Proper null checking implemented.

The null checking pattern for _jsRootObjectWrappersByFrameId is correctly implemented here, following the defensive pattern from the previous review.


304-329: Null checking and conditional logic look correct.

The method properly checks _jsRootObjectWrappersByFrameId for null at the start. The conditional creation logic for NETCOREAPP vs non-NETCOREAPP is sound, and passing a potentially null browserProcess parameter (line 323) appears intentional based on the constructor signature in JavascriptRootObjectWrapper.h.


425-435: Excellent defensive null handling for callbackRegistry.

This properly addresses the previous review comment about inconsistent null handling. The code safely retrieves callbackRegistry using a null-conditional pattern and includes appropriate null checking before use.


140-164: BindObjectAsyncHandler properly handles null rootObject — no fix needed.

The BindObjectAsyncHandler constructor accepts a potentially null JavascriptRootObjectWrapper^ parameter and safely handles it. In the Execute method (line 142–147 of BindObjectAsyncHandler.h), there is explicit null checking: if (Object::ReferenceEquals(_javascriptRootObjectWrapper, nullptr)) which sets an exception message and returns gracefully. Passing a null rootObject at line 159 is safe and will be handled correctly by the handler.


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

@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 (3)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (1)

375-394: Consider extracting duplicated root object creation logic.

The root object lookup and creation logic here (lines 381-393) duplicates GetJsRootObjectWrapper() (lines 304-328). While the inline approach works and handles the edge case where OnContextCreated wasn't called, consider calling GetJsRootObjectWrapper() to reduce duplication:

                     JavascriptRootObjectWrapper^ rootObjectWrapper;
                     _jsRootObjectWrappersByFrameId->TryGetValue(frameId, rootObjectWrapper);

-                    //NOTE: In the rare case when when OnContextCreated hasn't been called we need to manually create the rootObjectWrapper
-                    //It appears that OnContextCreated is only called for pages that have javascript on them, which makes sense
-                    //as without javascript there is no need for a context.
                     if (rootObjectWrapper == nullptr)
                     {
-#ifdef NETCOREAPP
-                        rootObjectWrapper = gcnew JavascriptRootObjectWrapper();
-#else
-                        auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());
-
-                        // If we cannot find the browserWrapper then we'll pass in nullptr which will disable
-                        // the use of sync bound objects (async will still work).
-                        rootObjectWrapper = gcnew JavascriptRootObjectWrapper(browserWrapper == nullptr ? nullptr : browserWrapper->BrowserProcess);
-#endif
-
-                        _jsRootObjectWrappersByFrameId->TryAdd(frameId, rootObjectWrapper);
+                        //NOTE: In the rare case when OnContextCreated hasn't been called we need to manually create the rootObjectWrapper
+                        rootObjectWrapper = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier());
                     }
CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h (2)

27-44: Make the JavascriptRootObjectWrapper dependency explicit in this header

BindObjectAsyncHandler now stores and uses JavascriptRootObjectWrapper^ (including calling Bind in this header), but this file doesn’t declare or include that type. That makes compilation depend on external include order in consuming translation units.

To keep the header self‑contained and avoid fragile ordering, consider explicitly including the wrapper’s header (or otherwise ensuring its declaration is available before this header is parsed), for example:

 #include "include/cef_v8.h"
 #include "RegisterBoundObjectRegistry.h"
 #include "..\CefSharp.Core.Runtime\Internals\Messaging\Messages.h"
 #include "..\CefSharp.Core.Runtime\Internals\Serialization\Primitives.h"
+ #include "JavascriptRootObjectWrapper.h"

If JavascriptRootObjectWrapper.h cannot be included here due to cycles, at minimum document the required include order where this header is used.


140-152: Clarify invariants around _javascriptRootObjectWrapper in the cached‑bind fast path

The cached‑objects fast path now hard‑depends on _javascriptRootObjectWrapper being non‑null and fails the call with a JS exception when it’s null:

if (Object::ReferenceEquals(_javascriptRootObjectWrapper, nullptr)) {
    exception = "BindObjectAsyncHandler::Execute - _javascriptRootObjectWrapper null, unable to bind objects";
    return true;
}

JavascriptRootObjectWrapper^ rootObject = _javascriptRootObjectWrapper;
rootObject->Bind(cachedObjects, context->GetGlobal());

If the design guarantees that a valid JavascriptRootObjectWrapper is always supplied when constructing this handler (which it likely should, given the new per‑frame management), it may be cleaner to enforce that at construction time (argument validation or debug assert) and treat a null here as an invariant violation (and log at ERROR level) rather than a “normal” runtime error path. That would make failures due to mis‑wiring or lifetime mismatches easier to detect during development.

If null is expected in some edge cases, consider adding a brief comment explaining when that can legitimately happen.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d699b6d and 13c87c0.

📒 Files selected for processing (7)
  • CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h (2 hunks)
  • CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (7 hunks)
  • CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h (3 hunks)
  • CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h (0 hunks)
  • CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp (1 hunks)
  • CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h (1 hunks)
  • CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1 hunks)
💤 Files with no reviewable changes (1)
  • CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-10T23:58:24.668Z
Learnt from: amaitland
Repo: cefsharp/CefSharp 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.

Applied to files:

  • CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h
🧬 Code graph analysis (4)
CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp (1)
CefSharp/Internals/JavascriptCallback.cs (1)
  • JavascriptCallback (38-54)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (1)
CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1)
  • JavascriptRootObjectWrapper (31-100)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h (1)
CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1)
  • JavascriptRootObjectWrapper (31-100)
CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h (2)
CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1)
  • JavascriptRootObjectWrapper (31-100)
CefSharp.BrowserSubprocess.Core/RegisterBoundObjectRegistry.h (1)
  • RegisterBoundObjectRegistry (22-62)
🔇 Additional comments (9)
CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp (1)

21-25: LGTM - Browser ID now derived from context.

Deriving BrowserId from context->GetBrowser()->GetIdentifier() at registration time is the correct approach for the frame-centric refactor. This ensures the callback captures the correct browser ID regardless of which frame's registry it was registered with.

CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h (1)

58-72: LGTM - Constructor simplified to remove browserId dependency.

The constructor changes correctly align with the refactored JavascriptCallbackRegistry. The non-NETCOREAPP path still receives IBrowserProcess^ for synchronous bound object support, which is appropriate.

CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h (1)

28-32: LGTM - Parameterless constructor aligns with frame-centric design.

The removal of browserId from the constructor is correct. The browser ID is now derived from the V8 context at callback registration time (in Register()), which is the appropriate location given the new frame-based architecture.

CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (4)

159-159: LGTM - BindObjectAsyncHandler now receives rootObject directly.

Passing JavascriptRootObjectWrapper^ instead of CefBrowserWrapper^ aligns with the frame-centric refactor. The rootObject is obtained from GetJsRootObjectWrapper() at line 140, which handles the lookup/creation appropriately.


223-236: LGTM - Proper cleanup on context release.

The cleanup logic correctly:

  1. Captures a local reference to avoid race conditions
  2. Performs null check before proceeding
  3. Removes and deletes the wrapper when the frame's context is released

This ensures proper resource management per frame lifecycle.


304-328: LGTM - GetJsRootObjectWrapper correctly manages frame-based root objects.

The implementation properly:

  1. Looks up existing wrapper by frame ID
  2. Creates a new wrapper if not found (lazy initialization)
  3. Handles the non-NETCOREAPP case where BrowserProcess is still needed for sync bound objects
  4. Returns nullptr gracefully when browserWrapper is unavailable

The callers appropriately handle the nullptr case (e.g., line 144, 607).


468-470: LGTM - Consistent usage of frame-based root object lookup.

The callback request handler correctly looks up the root object by frame ID and handles the null case gracefully (lines 470-474).

CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h (2)

29-29: LGTM - New frame-based root object map declaration.

The addition of _jsRootObjectWrappersByFrameId as a ConcurrentDictionary<String^, JavascriptRootObjectWrapper^>^ correctly supports the frame-centric architecture change.


51-52: LGTM - Proper initialization of the new map.

The _jsRootObjectWrappersByFrameId is correctly initialized as a new ConcurrentDictionary in the constructor.

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: 2

Caution

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

⚠️ Outside diff range comments (3)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (3)

304-323: Potential race condition in lazy initialization.

The TryGetValue + TryAdd pattern is not atomic. If multiple threads call GetJsRootObjectWrapper concurrently for the same frameId, both may create a JavascriptRootObjectWrapper, but only one will be added to the dictionary. The method would then return different instances to different callers, potentially causing inconsistent state.

Consider using a thread-safe lazy initialization pattern. Apply this diff to use GetOrAdd:

 JavascriptRootObjectWrapper^ CefAppUnmanagedWrapper::GetJsRootObjectWrapper(int browserId, CefString& frameId)
 {
     auto rootObjectWrappers = _jsRootObjectWrappersByFrameId;
     auto frameIdClr = StringUtils::ToClr(frameId);
 
-    JavascriptRootObjectWrapper^ rootObject;
-    if (!rootObjectWrappers->TryGetValue(frameIdClr, rootObject))
-    {
+    auto rootObject = rootObjectWrappers->GetOrAdd(frameIdClr, gcnew Func<String^, JavascriptRootObjectWrapper^>(
+        [this, browserId](String^ key) -> JavascriptRootObjectWrapper^ {
 #ifdef NETCOREAPP
-        rootObject = gcnew JavascriptRootObjectWrapper();
+            return gcnew JavascriptRootObjectWrapper();
 #else
-        auto browserWrapper = FindBrowserWrapper(browserId);
-
-        rootObject = gcnew JavascriptRootObjectWrapper(browserWrapper == nullptr ? nullptr : browserWrapper->BrowserProcess);
+            auto browserWrapper = FindBrowserWrapper(browserId);
+            return gcnew JavascriptRootObjectWrapper(browserWrapper == nullptr ? nullptr : browserWrapper->BrowserProcess);
 #endif
-        rootObjectWrappers->TryAdd(frameIdClr, rootObject);
-    }
+        }
+    ));
 
     return rootObject;
 }

304-323: Recommend comprehensive testing for this refactoring.

This refactoring fundamentally changes how JavascriptRootObjectWrapper instances are managed (from per-browser to per-frame). Given the complexity and the multiple code paths involved (legacy binding, JS binding API, message handling, context lifecycle), comprehensive testing is essential to ensure:

  1. Correct behavior when frames are created/destroyed
  2. Thread safety under concurrent access
  3. Proper cleanup and no memory leaks
  4. Correct operation in popup scenarios
  5. Behavior when contexts are created without prior browser creation

The PR checklist indicates testing is "TBA". Please ensure thorough testing before merging.


140-163: Based on my verification of the code, I have confirmed the review comment's findings. Let me now generate the rewritten review comment:


Unnecessary null checking for rootObject due to implementation guarantee.

GetJsRootObjectWrapper (lines 304-323) always creates and returns a JavascriptRootObjectWrapper, never returning nullptr. Both code paths—when the wrapper is found in cache or newly created—guarantee a non-null return. However, line 144 performs a null check (rootObject != nullptr), while line 159 passes rootObject to BindObjectAsyncHandler without any null check, and line 163 includes a ternary operator for null checking. This inconsistent null handling suggests the null check at line 144 is unnecessary defensive code. Remove it to accurately reflect the implementation guarantee.

🧹 Nitpick comments (1)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (1)

368-371: Update comment to reflect new lazy initialization.

The comment references the "rare case when OnContextCreated hasn't been called" and explains that OnContextCreated is only called for pages with JavaScript. While this is accurate, the comment could be clearer given the refactoring. GetJsRootObjectWrapper now provides lazy initialization and is called both in OnContextCreated (line 140) and here, ensuring a rootObjectWrapper always exists when needed.

Consider updating the comment to:

-//NOTE: In the rare case when when OnContextCreated hasn't been called we need to manually create the rootObjectWrapper
-//It appears that OnContextCreated is only called for pages that have javascript on them, which makes sense
-//as without javascript there is no need for a context.
+//NOTE: GetJsRootObjectWrapper lazily creates the rootObjectWrapper if it doesn't exist.
+//This handles cases where OnContextCreated hasn't been called (e.g., for pages without JavaScript).
 JavascriptRootObjectWrapper^ rootObjectWrapper = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier());
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13c87c0 and da9cbe5.

📒 Files selected for processing (1)
  • CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (7 hunks)
🔇 Additional comments (1)
CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp (1)

315-317: _browserProcess null handling is properly implemented with defensive checks.

The _browserProcess field is only dereferenced in the Bind method (line 35 of JavascriptRootObjectWrapper.cpp), where an explicit null check is performed before any usage. When _browserProcess is nullptr, the code logs an ERROR message and skips that object with a continue statement, preventing any null dereference issues. The null-handling is correct and safe.

@AppVeyorBot
Copy link

@amaitland amaitland changed the title Core - Rewrite RenderProcess to avoid using Core - Rewrite RenderProcess to avoid using OnBrowserDestroyed for JS eval Nov 30, 2025
@AppVeyorBot
Copy link

@amaitland amaitland mentioned this pull request Nov 30, 2025
9 tasks
@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)

60-60: Optionally dispose the IFrame when reading Identifier

For consistency with the earlier using (var frame = browser.GetMainFrame()) pattern and to dispose the IFrame wrapper promptly, you could wrap this call in a using:

-                var frameId = browser.GetMainFrame().Identifier;
+                long frameId;
+                using (var frame = browser.GetMainFrame())
+                {
+                    frameId = frame.Identifier;
+                }

Not critical for the test, but slightly cleaner and consistent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a347f5 and a07b5e1.

📒 Files selected for processing (2)
  • CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1 hunks)
  • CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs (1)
CefSharp.Example/CefExample.cs (1)
  • CefExample (15-274)
CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1)
CefSharp.Example/CefExample.cs (1)
  • CefExample (15-274)
🔇 Additional comments (2)
CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs (1)

97-100: Explicit navigation improves test determinism

Loading BindingApiCustomObjectNameTestUrl explicitly and asserting loadResponse.Success before evaluating the script makes this test independent of prior browser state and should reduce flakiness. The change looks correct and consistent with patterns used elsewhere in the suite.

CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1)

56-69: Frame ID capture and frame-scoped assertion look correct

Capturing frameId from browser.GetMainFrame().Identifier before reloading, then asserting the error message starts with "Frame with Id: {frameId}", aligns the test with the new per-frame callback handling and keeps it robust via Assert.StartsWith while allowing message suffix changes.

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)

60-69: Frame‑Id based assertion looks correct; consider disposing the frame instance

Switching the assertion to use frameId and expecting "Frame with Id:{frameId}" aligns with the new frame‑scoped error messaging and looks logically correct. One minor improvement: the additional browser.GetMainFrame() at Line 60 is not disposed, whereas earlier in the test you use a using block for the frame. For consistency and to avoid holding onto the wrapper longer than needed, you could grab the identifier via a short using:

-                var callbackExecuteCancelAfterV8ContextTask = callbackExecuteCancelAfterV8ContextCallback.ExecuteAsync();
-                var frameId = browser.GetMainFrame().Identifier;
+                var callbackExecuteCancelAfterV8ContextTask = callbackExecuteCancelAfterV8ContextCallback.ExecuteAsync();
+                int frameId;
+                using (var frame = browser.GetMainFrame())
+                {
+                    frameId = frame.Identifier;
+                }

This keeps the behavior the same while matching the existing disposal pattern in the test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a07b5e1 and c052def.

📒 Files selected for processing (1)
  • CefSharp.Test/Javascript/JavascriptCallbackTests.cs (1 hunks)

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@amaitland amaitland merged commit d78d01a into master Dec 3, 2025
2 of 3 checks passed
@amaitland amaitland deleted the rewrite/browserdestroyed branch December 3, 2025 09:00
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