cefsharp / CefSharp

.NET (WPF and Windows Forms) bindings for the Chromium Embedded Framework
http://cefsharp.github.io/
Other
9.84k stars 2.92k forks source link

CefSharp.WinForms.Example.netcore crashes when popup window #4656

Open gearsns opened 9 months ago

gearsns commented 9 months ago

Is there an existing issue for this?

CefSharp Version

120.1.80

Operating System

Windows 11

Architecture

x64

.Net Version

.Net 6.0

Implementation

WinForms

Reproduction Steps

  1. Run CefSharp.WinForms.Example.netcore
  2. Show popup window
  3. Close popup window
  4. Repeat 2 - 3.

Expected behavior

Expectations is that it will not crash when you close the popup window

Actual behavior

crash

Regression?

No response

Known Workarounds

Is this due to DoClose in LifeSpanHandler.cs? Excluding control.Dispose() from InnvokeSyncOnUiThreadIfRequired stops crashing.

Does this problem also occur in the CEF Sample Application

No

Other information

html source

<!DOCTYPE HTML>
<html>
<a href="javascript:popup_open()" id="open">Popup!</a>
<a href="javascript:popup_close()" id="close">Close!</a>
<script>
    const popup_open = () => {
        window.open("index.html?pop=on")
    }
    const popup_close = () => {
        parent.window.close()
    }
    const params = new URLSearchParams(document.location.search)
    if (params.has("pop")) {
        document.getElementById("open").style.display = "none"
        document.title = "Popup!!"
    } else {
        document.getElementById("close").style.display = "none"
        document.title = "Popup Main"
    }
</script>
</html>

edit source code CefSharp.WinForms\Handler\LifeSpanhandler.cs

                        control.InvokeSyncOnUiThreadIfRequired(new Action(() =>
                        {
                            onPopupDestroyed?.Invoke(control, browser);

                            //control.Dispose(); // Comment out!!
                        }));
                        // Add
                        control.InvokeOnUiThreadIfRequired(new Action(() =>
                        {
                            control.Dispose();
                        }));

CefSharp.WinForms.Example\BrowserTabUserControl.cs

                .OnPopupDestroyed((ctrl, popupBrowser) =>
                {
                    //If we docked  DevTools (hosted it ourselves rather than the default popup)
                    //Used when the BrowserTabUserControl.ShowDevToolsDocked method is called
                    if (popupBrowser.MainFrame.Url.Equals("devtools://devtools/devtools_app.html"))
                    {
                        //Dispose of the parent control we used to host DevTools, this will release the DevTools window handle
                        //and the ILifeSpanHandler.OnBeforeClose() will be call after.
                        ctrl.Dispose();
                    }
                    else
                    {
                        //If browser is disposed or the handle has been released then we don't
                        //need to remove the tab in this example. The user likely used the
                        // File -> Close Tab menu option which also calls BrowserForm.RemoveTab
                        if (!ctrl.IsDisposed && ctrl.IsHandleCreated)
                        {
                            if (ctrl.FindForm() is BrowserForm owner)
                            {
                                owner.RemoveTab(ctrl);
                            }

                            //ctrl.Dispose(); // Comment out!!
                        }
                    }
                })
amaitland commented 9 months ago

This should really be fixed upstream in CEF.

https://github.com/chromiumembedded/cef/blob/e4acacee18d66d9bfe65a15072307d619117bd32/libcef/browser/alloy/alloy_browser_host_impl.cc#L624

amaitland commented 9 months ago

What is the call stack for the exception?

gearsns commented 9 months ago

Since Dispose is called in DoClose, shouldn't it be fixed on the CefSharp side, not on the cef side?

The call stack for the exception is as follows:

    libcef.dll!AlloyBrowserHostImpl::DestroyBrowser() Line 600  C++
    libcef.dll!AlloyBrowserHostImpl::CloseContents(content::WebContents * source) Line 1040 C++
    [Inline Frame] libcef.dll!base::OnceCallback<void ()>::Run() Line 154   C++
    libcef.dll!base::TaskAnnotator::RunTaskImpl(base::PendingTask & pending_task) Line 201  C++
    [Inline Frame] libcef.dll!base::TaskAnnotator::RunTask(perfetto::StaticString event_name, base::PendingTask & pending_task, base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl::<lambda_0> && args) Line 89  C++
    [Inline Frame] libcef.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow * continuation_lazy_now) Line 461 C++
    libcef.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() Line 326 C++
    libcef.dll!base::MessagePumpForUI::DoRunLoop() Line 214 C++
    libcef.dll!base::MessagePumpWin::Run(base::MessagePump::Delegate * delegate) Line 80    C++
    libcef.dll!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool application_tasks_allowed, base::TimeDelta timeout) Line 629 C++
    libcef.dll!base::RunLoop::Run(const base::Location & location) Line 136 C++
>   [Inline Frame] libcef.dll!CefMainRunner::RunMessageLoop() Line 312  C++
    libcef.dll!CefUIThread::ThreadMain() Line 182   C++
    libcef.dll!base::`anonymous namespace'::ThreadFunc(void * params) Line 133  C++
    kernel32.dll!00007ff81e64257d() Unknown
    ntdll.dll!00007ff81feeaa58()    Unknown
amaitland commented 9 months ago

Since Dispose is called in DoClose, shouldn't it be fixed on the CefSharp side, not on the cef side?

For me that's just hacking around the issue. Calling Dispose (which calls CefBrowserHost::CloseBrowser) works most of the time, there's a threading issue which means it throws some of the time. Even if we move the call in the same code, that won't fix user code.

The LifespanHandler provided is just a default implementation. You can override the behaviour of the default implementation to workaround the issue in your own code.

If the CEF maintainer deems this as won't fix, then we can look at working around the issue.

amaitland commented 7 months ago

https://www.magpcss.org/ceforum/viewtopic.php?f=10&t=19703