// Copyright © 2023 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
using System;
using System.Windows.Forms;
using CefSharp.WinForms.Host;
namespace CefSharp.WinForms.Example.Handlers
{
///
/// A WinForms Specific implementation that demos
/// the process of hosting a Popup using a instance.
/// This implementation returns true in
/// so no WM_CLOSE message is sent, this differs from the default CEF behaviour.
///
internal class WinFormsLifeSpanHandlerEx : CefSharp.Handler.LifeSpanHandler
{
private Action onPopupBrowserCreated;
private Action onPopupDestroyed;
private Action onPopupCreated;
///
/// The delegate will be called when the underlying CEF has been
/// created. The instance is valid until
/// is called. provides low level access to the CEF Browser, you can access frames, view source,
/// perform navigation (via frame) etc. This is equivilent to the .
///
/// Action to be invoked when the has been created.
/// instance allowing you to chain method calls together
public WinFormsLifeSpanHandlerEx OnPopupBrowserCreated(Action onPopupBrowserCreated)
{
this.onPopupBrowserCreated = onPopupBrowserCreated;
return this;
}
///
/// The will be called when the is to be
/// removed from it's parent.
/// When the is called you must remove/dispose of the .
///
/// Action to be invoked when the Popup is to be destroyed.
/// instance allowing you to chain method calls together
public WinFormsLifeSpanHandlerEx OnPopupDestroyed(Action onPopupDestroyed)
{
this.onPopupDestroyed = onPopupDestroyed;
return this;
}
///
/// The will be called when the has been
/// created. When the is called you must add the control to it's intended parent.
///
/// Action to be invoked when the Popup host has been created and is ready to be attached to it's parent.
/// instance allowing you to chain method calls together
public WinFormsLifeSpanHandlerEx OnPopupCreated(Action onPopupCreated)
{
this.onPopupCreated = onPopupCreated;
return this;
}
///
protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
if (browser.IsPopup)
{
var control = ChromiumHostControlBase.FromBrowser(browser);
//We don't have a parent control so we allow the default behaviour, required to close
//default popups e.g. DevTools
if (control == null)
{
return false;
}
//If the main browser is disposed or the handle has been released then we don't
//need to remove the popup (likely removed from menu)
if (!control.IsDisposed && control.IsHandleCreated)
{
try
{
control.BeginInvoke(new Action(() =>
{
onPopupDestroyed?.Invoke(control);
control.Dispose();
}));
}
catch (ObjectDisposedException)
{
// If the popup is being hosted on a Form that is being
// Closed/Disposed as we attempt to call Control.BeginInvoke
// we can end up with an ObjectDisposedException
// return false (Default behaviour).
return false;
}
}
}
//No WM_CLOSE message will be sent, manually handle closing
return true;
}
///
protected override void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
if (browser.IsPopup)
{
var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
webBrowser.BeginInvoke((Action) (() =>
{
var control = ChromiumHostControlBase.FromBrowser(browser);
if (control != null)
{
onPopupBrowserCreated?.Invoke(control);
}
}));
}
}
///
protected override void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
if (!browser.IsDisposed && browser.IsPopup)
{
}
}
///
///
/// NOTE: DevTools popups DO NOT trigger OnBeforePopup.
///
protected override bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
newBrowser = null;
var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
ChromiumWebBrowser control = null;
//We need to execute sync here so IWindowInfo.SetAsChild is called before we return false;
webBrowser.Invoke(new Action(() =>
{
control = new ChromiumWebBrowser
{
Dock = DockStyle.Fill
};
//NOTE: This is important and must be called before the handle is created
control.SetAsPopup();
control.LifeSpanHandler = this;
control.CreateControl();
var rect = control.ClientRectangle;
var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height);
windowInfo.SetAsChild(control.Handle, windowBounds);
onPopupCreated?.Invoke(control, targetUrl, targetFrameName, windowBounds);
}));
newBrowser = control;
return false;
}
}
}