@@ -321,5 +321,292 @@ public async Task ShouldWorkWhenExecutedMultipleTimes()
321321 Assert . Equal ( 42 , callbackResponse . Result ) ;
322322 }
323323 }
324+
325+ [ Fact ]
326+ public async Task ShouldHandleCallbackAfterMultipleContextChanges ( )
327+ {
328+ AssertInitialLoadComplete ( ) ;
329+
330+ // Test that callbacks are properly cleaned up after multiple context changes
331+ var javascriptResponse1 = await Browser . EvaluateScriptAsync ( "(function() { return Promise.resolve(42); })" ) ;
332+ Assert . True ( javascriptResponse1 . Success ) ;
333+ var callback1 = ( IJavascriptCallback ) javascriptResponse1 . Result ;
334+
335+ // Change context
336+ await Browser . LoadUrlAsync ( CefExample . HelloWorldUrl ) ;
337+
338+ var javascriptResponse2 = await Browser . EvaluateScriptAsync ( "(function() { return Promise.resolve(84); })" ) ;
339+ Assert . True ( javascriptResponse2 . Success ) ;
340+ var callback2 = ( IJavascriptCallback ) javascriptResponse2 . Result ;
341+
342+ // Execute the new callback - should work
343+ var callbackResponse2 = await callback2 . ExecuteAsync ( ) ;
344+ Assert . True ( callbackResponse2 . Success ) ;
345+ Assert . Equal ( 84 , callbackResponse2 . Result ) ;
346+
347+ // Old callback should fail gracefully
348+ var callbackResponse1 = await callback1 . ExecuteAsync ( ) ;
349+ Assert . False ( callbackResponse1 . Success ) ;
350+ Assert . Contains ( "Frame with Id:" , callbackResponse1 . Message ) ;
351+ }
352+
353+ [ Fact ]
354+ public async Task ShouldProperlyCleanupCallbacksOnFrameDestruction ( )
355+ {
356+ using ( var browser = new CefSharp . OffScreen . ChromiumWebBrowser ( automaticallyCreateBrowser : false ) )
357+ {
358+ await browser . CreateBrowserAsync ( ) ;
359+ await browser . LoadUrlAsync ( CefExample . HelloWorldUrl ) ;
360+
361+ var javascriptResponse = await browser . EvaluateScriptAsync ( "(function() { return Promise.resolve('test'); })" ) ;
362+ Assert . True ( javascriptResponse . Success ) ;
363+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
364+ var frameId = browser . GetMainFrame ( ) . Identifier ;
365+
366+ // Execute callback successfully first
367+ var result1 = await callback . ExecuteAsync ( ) ;
368+ Assert . True ( result1 . Success ) ;
369+ Assert . Equal ( "test" , result1 . Result ) ;
370+
371+ // Load new page to destroy frame
372+ await browser . LoadUrlAsync ( "about:blank" ) ;
373+
374+ // Callback should now fail with frame-specific error
375+ var result2 = await callback . ExecuteAsync ( ) ;
376+ Assert . False ( result2 . Success ) ;
377+ Assert . Contains ( $ "Frame with Id:{ frameId } ", result2 . Message ) ;
378+ }
379+ }
380+
381+ [ Fact ]
382+ public async Task ShouldHandleCallbacksFromDifferentFrames ( )
383+ {
384+ using ( var browser = new CefSharp . OffScreen . ChromiumWebBrowser ( automaticallyCreateBrowser : false ) )
385+ {
386+ await browser . CreateBrowserAsync ( ) ;
387+
388+ // Load a page with iframe
389+ await browser . LoadHtmlAsync ( @"
390+ <html>
391+ <body>
392+ <h1>Main Frame</h1>
393+ <iframe id='testFrame' src='about:blank'></iframe>
394+ </body>
395+ </html>" ) ;
396+
397+ // Create callback in main frame
398+ var mainFrameResponse = await browser . EvaluateScriptAsync ( "(function() { return Promise.resolve('main'); })" ) ;
399+ Assert . True ( mainFrameResponse . Success ) ;
400+ var mainCallback = ( IJavascriptCallback ) mainFrameResponse . Result ;
401+
402+ // Execute main frame callback
403+ var mainResult = await mainCallback . ExecuteAsync ( ) ;
404+ Assert . True ( mainResult . Success ) ;
405+ Assert . Equal ( "main" , mainResult . Result ) ;
406+ }
407+ }
408+
409+ [ Theory ]
410+ [ InlineData ( "(function() { return Promise.resolve(null); })" , null ) ]
411+ [ InlineData ( "(function() { return Promise.resolve(undefined); })" , null ) ]
412+ public async Task ShouldHandleNullAndUndefinedCallbackResults ( string script , object expected )
413+ {
414+ AssertInitialLoadComplete ( ) ;
415+
416+ var javascriptResponse = await Browser . EvaluateScriptAsync ( script ) ;
417+ Assert . True ( javascriptResponse . Success ) ;
418+
419+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
420+ var callbackResponse = await callback . ExecuteAsync ( ) ;
421+
422+ Assert . True ( callbackResponse . Success ) ;
423+ Assert . Equal ( expected , callbackResponse . Result ) ;
424+ }
425+
426+ [ Fact ]
427+ public async Task ShouldHandleNestedCallbackExecution ( )
428+ {
429+ AssertInitialLoadComplete ( ) ;
430+
431+ // Create a callback that returns another function
432+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
433+ (function() {
434+ return function(x) {
435+ return Promise.resolve(x * 2);
436+ };
437+ })" ) ;
438+ Assert . True ( javascriptResponse . Success ) ;
439+
440+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
441+
442+ // Execute with parameter
443+ var callbackResponse = await callback . ExecuteAsync ( 21 ) ;
444+ Assert . True ( callbackResponse . Success ) ;
445+ Assert . Equal ( 42 , callbackResponse . Result ) ;
446+ }
447+
448+ [ Fact ]
449+ public async Task ShouldHandleCallbackExecutionWithComplexObjects ( )
450+ {
451+ AssertInitialLoadComplete ( ) ;
452+
453+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
454+ (function(obj) {
455+ return Promise.resolve({
456+ doubled: obj.value * 2,
457+ message: 'Result: ' + obj.value
458+ });
459+ })" ) ;
460+ Assert . True ( javascriptResponse . Success ) ;
461+
462+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
463+
464+ var inputObj = new { value = 42 } ;
465+ var callbackResponse = await callback . ExecuteAsync ( inputObj ) ;
466+
467+ Assert . True ( callbackResponse . Success ) ;
468+ dynamic result = callbackResponse . Result ;
469+ Assert . Equal ( 84 , ( int ) result . doubled ) ;
470+ Assert . Equal ( "Result: 42" , ( string ) result . message ) ;
471+ }
472+
473+ [ Theory ]
474+ [ InlineData ( 1 ) ]
475+ [ InlineData ( 5 ) ]
476+ [ InlineData ( 10 ) ]
477+ public async Task ShouldHandleMultipleSequentialCallbackExecutions ( int executionCount )
478+ {
479+ AssertInitialLoadComplete ( ) ;
480+
481+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
482+ (function(x) {
483+ return Promise.resolve(x + 1);
484+ })" ) ;
485+ Assert . True ( javascriptResponse . Success ) ;
486+
487+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
488+
489+ for ( var i = 0 ; i < executionCount ; i ++ )
490+ {
491+ var callbackResponse = await callback . ExecuteAsync ( i ) ;
492+ Assert . True ( callbackResponse . Success ) ;
493+ Assert . Equal ( i + 1 , callbackResponse . Result ) ;
494+ }
495+ }
496+
497+ [ Fact ]
498+ public async Task ShouldHandleCallbackWithLongRunningOperation ( )
499+ {
500+ AssertInitialLoadComplete ( ) ;
501+
502+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
503+ (function() {
504+ return new Promise(resolve => {
505+ setTimeout(() => resolve('completed'), 2000);
506+ });
507+ })" ) ;
508+ Assert . True ( javascriptResponse . Success ) ;
509+
510+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
511+
512+ var callbackResponse = await callback . ExecuteAsync ( ) ;
513+ Assert . True ( callbackResponse . Success ) ;
514+ Assert . Equal ( "completed" , callbackResponse . Result ) ;
515+ }
516+
517+ [ Fact ]
518+ public async Task ShouldHandleCallbackErrorsGracefully ( )
519+ {
520+ AssertInitialLoadComplete ( ) ;
521+
522+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
523+ (function() {
524+ return Promise.reject(new Error('Custom error message'));
525+ })" ) ;
526+ Assert . True ( javascriptResponse . Success ) ;
527+
528+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
529+ var callbackResponse = await callback . ExecuteAsync ( ) ;
530+
531+ Assert . False ( callbackResponse . Success ) ;
532+ Assert . Contains ( "Custom error message" , callbackResponse . Message ) ;
533+ }
534+
535+ [ Fact ]
536+ public async Task ShouldVerifyCallbackRegistryCleanup ( )
537+ {
538+ // Test that callbacks are properly cleaned up when context is released
539+ using ( var browser = new CefSharp . OffScreen . ChromiumWebBrowser ( automaticallyCreateBrowser : false ) )
540+ {
541+ await browser . CreateBrowserAsync ( ) ;
542+ await browser . LoadUrlAsync ( CefExample . HelloWorldUrl ) ;
543+
544+ var callbacks = new List < IJavascriptCallback > ( ) ;
545+
546+ // Create multiple callbacks
547+ for ( int i = 0 ; i < 5 ; i ++ )
548+ {
549+ var response = await browser . EvaluateScriptAsync ( $ "(function() {{ return Promise.resolve({ i } ); }})") ;
550+ Assert . True ( response . Success ) ;
551+ callbacks . Add ( ( IJavascriptCallback ) response . Result ) ;
552+ }
553+
554+ // Verify all callbacks work
555+ for ( int i = 0 ; i < callbacks . Count ; i ++ )
556+ {
557+ var result = await callbacks [ i ] . ExecuteAsync ( ) ;
558+ Assert . True ( result . Success ) ;
559+ Assert . Equal ( i , result . Result ) ;
560+ }
561+
562+ // Destroy context
563+ await browser . LoadUrlAsync ( "about:blank" ) ;
564+
565+ // Verify all callbacks are now invalid
566+ foreach ( var callback in callbacks )
567+ {
568+ var result = await callback . ExecuteAsync ( ) ;
569+ Assert . False ( result . Success ) ;
570+ }
571+ }
572+ }
573+
574+ [ Fact ]
575+ public async Task ShouldHandleCallbackWithArrayParameter ( )
576+ {
577+ AssertInitialLoadComplete ( ) ;
578+
579+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
580+ (function(arr) {
581+ return Promise.resolve(arr.reduce((a, b) => a + b, 0));
582+ })" ) ;
583+ Assert . True ( javascriptResponse . Success ) ;
584+
585+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
586+ var callbackResponse = await callback . ExecuteAsync ( new [ ] { 1 , 2 , 3 , 4 , 5 } ) ;
587+
588+ Assert . True ( callbackResponse . Success ) ;
589+ Assert . Equal ( 15 , callbackResponse . Result ) ;
590+ }
591+
592+ [ Fact ]
593+ public async Task ShouldHandleCallbackReturningArray ( )
594+ {
595+ AssertInitialLoadComplete ( ) ;
596+
597+ var javascriptResponse = await Browser . EvaluateScriptAsync ( @"
598+ (function() {
599+ return Promise.resolve([1, 2, 3, 4, 5]);
600+ })" ) ;
601+ Assert . True ( javascriptResponse . Success ) ;
602+
603+ var callback = ( IJavascriptCallback ) javascriptResponse . Result ;
604+ var callbackResponse = await callback . ExecuteAsync ( ) ;
605+
606+ Assert . True ( callbackResponse . Success ) ;
607+ var resultArray = callbackResponse . Result as object [ ] ;
608+ Assert . NotNull ( resultArray ) ;
609+ Assert . Equal ( 5 , resultArray . Length ) ;
610+ }
324611 }
325612}
0 commit comments