Skip to content

Fix invoking JavaScript with C# DOM arguments - #127

Merged
FlorianRappl merged 1 commit into
AngleSharp:develfrom
lahma:jint-invoke
Jul 27, 2026
Merged

Fix invoking JavaScript with C# DOM arguments#127
FlorianRappl merged 1 commit into
AngleSharp:develfrom
lahma:jint-invoke

Conversation

@lahma

@lahma lahma commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #76

Cause

JsValue.FromObject is the conversion a C# caller reaches, and it is the one path that does not go through EngineExtensions.ToJsValue. Jint therefore wrapped an AngleSharp DOM object in its ordinary CLR ObjectWrapper rather than in the DOM proxy, and that wrapper is opaque to this library's own marshalling: JsValueExtensions.FromJsValue only unwraps a DomNodeInstance, so the argument came back out as the wrapper itself, AsComplex found no conversion from ObjectWrapper to INode, and the call died with JavaScriptException: [Internal] Could not find corresponding cast target. — the exception reported in the issue. Short of that, script also saw the CLR member surface (TextContent, AppendChild) instead of the DOM one, and the object was not the one document.querySelector hands back for the same node.

Fix

EngineInstance installs a Jint WrapObjectHandler, the hook Jint consults whenever it wraps a CLR object. An object that resolves to a DOM name is converted with GetDomNode, so it goes through the same ReferenceCache the rest of the engine uses and one node keeps one JS object no matter which side hands it over. Everything else keeps Jint's ObjectWrapper, which is what a host object registered through JsScriptingService.External is expected to be.

PrototypeTypeCache.IsDomType is what decides, caching the answer per type since it depends on the type alone. The name may be inherited — a b element answers true through HTMLElement, which is exactly what makes the DOM view the right one for it — but a bare EventTarget is not enough: AngleSharp hangs HtmlParser, BrowsingContext and the requesters off the very same base class, and those are host objects that have to keep their CLR members.

Behaviour change

An AngleSharp DOM object registered in JsScriptingService.External now reaches script as the DOM object rather than as a CLR wrapper, so script uses doc.querySelector(…) where it previously used doc.QuerySelector(…). A collection handed over from C# likewise becomes the DOM collection, which means the array-like conveniences Jint's wrapper gave it (Symbol.iterator, the Array.from fast path) no longer apply — the same gap collections obtained from script already have. Host objects that are not part of the DOM are untouched.

Tests

InteractionTests covers the issue's repro, the DOM member surface on a C#-created element, the JS identity of one node across the C# and the script path, and that a non-DOM AngleSharp host object keeps its CLR members.

@FlorianRappl FlorianRappl 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.

LGTM!

@FlorianRappl

Copy link
Copy Markdown
Contributor

Quick question @lahma: Is this ready or is there anything I can help with / contribute to?

BTW: Many thanks - you are my hero 🦸 !

@lahma

lahma commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

I'm doing some review rounds for optimal solution, this is on the hot path. Stay tuned!

BTW: Many thanks - you are my hero 🦸 !

Happy to help ❤️

@FlorianRappl

Copy link
Copy Markdown
Contributor

Just FYI - after this all issues are essentially gone. My plan is to introduce some more tests (WPT), round it all up and then have the 1.0.0 (until Thursday, or earlier).

This is, of course, not the end of AngleSharp.Js (I rather think this is all the beginning...), but its a major milestone and a massive step forward.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lahma

lahma commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Awesome. I'm building a new version v4.15 of Jint and the big headline is making interop easier and faster. You project is one of the explored integrators so I will have one PR shaping the "optimal" integration soon'ish. Release preparations take time so it might be today or tomorrow...

@lahma

lahma commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

The fix was here a bit more complex than the first try, so ready for review now.

@lahma
lahma marked this pull request as ready for review July 27, 2026 13:24

@FlorianRappl FlorianRappl 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.

Awesome - life goal achieved 🥇

Many thanks for all the hard work.

@FlorianRappl
FlorianRappl merged commit 1f83b57 into AngleSharp:devel Jul 27, 2026
5 checks passed
@lahma
lahma deleted the jint-invoke branch July 27, 2026 13:32
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.

JavaScript exception when trying to call JavaScript function from C# code with Invoke, passing in element node created in C#

2 participants