// 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; } } }