// Copyright © 2021 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.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CefSharp.WinForms.Host
{
///
/// Chromium Browser Host Control, used for hosting Popups in WinForms
///
///
[Docking(DockingBehavior.AutoDock), ToolboxBitmap(typeof(ChromiumWebBrowser)),
Designer(typeof(ChromiumWebBrowserDesigner))]
public class ChromiumHostControl : ChromiumHostControlBase, IWinFormsChromiumWebBrowser
{
///
/// Get access to the core instance.
/// Maybe null if the underlying CEF Browser has not yet been
/// created or if this control has been disposed. Check
/// before accessing.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public IBrowser BrowserCore { get; internal set; }
///
/// Event handler that will get called when the resource load for a navigation fails or is canceled.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
public event EventHandler LoadError;
///
/// Event handler that will get called when the browser begins loading a frame. Multiple frames may be loading at the same
/// time. Sub-frames may start or continue loading after the main frame load has ended. This method may not be called for a
/// particular frame if the load request for that frame fails. For notification of overall browser load status use
/// OnLoadingStateChange instead.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
/// Whilst this may seem like a logical place to execute js, it's called before the DOM has been loaded, implement
/// as it's called when the underlying V8Context is created
///
public event EventHandler FrameLoadStart;
///
/// Event handler that will get called when the browser is done loading a frame. Multiple frames may be loading at the same
/// time. Sub-frames may start or continue loading after the main frame load has ended. This method will always be called
/// for all frames irrespective of whether the request completes successfully.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
public event EventHandler FrameLoadEnd;
///
/// Event handler that will get called when the Loading state has changed.
/// This event will be fired twice. Once when loading is initiated either programmatically or
/// by user action, and once when loading is terminated due to completion, cancellation of failure.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
public event EventHandler LoadingStateChanged;
///
/// Event handler for receiving Javascript console messages being sent from web pages.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
/// (The exception to this is when you're running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
///
public event EventHandler ConsoleMessage;
///
/// Event handler for changes to the status message.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
/// (The exception to this is when you're running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
///
public event EventHandler StatusMessage;
///
/// Occurs when the browser address changed.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
public event EventHandler AddressChanged;
///
/// Occurs when the browser title changed.
/// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
/// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
/// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
///
public event EventHandler TitleChanged;
///
/// A flag that indicates whether the control is currently loading one or more web pages (true) or not (false).
///
/// true if this instance is loading; otherwise, false.
/// In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool IsLoading { get; private set; }
///
/// The address (URL) which the browser control is currently displaying.
/// Will automatically be updated as the user navigates to another page (e.g. by clicking on a link).
///
/// The address.
/// In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public string Address { get; private set; }
///
/// A flag that indicates whether the state of the control currently supports the GoForward action (true) or not (false).
///
/// true if this instance can go forward; otherwise, false.
/// In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool CanGoForward { get; private set; }
///
/// A flag that indicates whether the state of the control current supports the GoBack action (true) or not (false).
///
/// true if this instance can go back; otherwise, false.
/// In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool CanGoBack { get; private set; }
///
/// A flag that indicates whether the WebBrowser is initialized (true) or not (false).
///
/// true if this instance is browser initialized; otherwise, false.
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool IsBrowserInitialized
{
get { return BrowserCore != null; }
}
///
/// Handles the event.
///
/// The instance containing the event data.
internal void OnFrameLoadStart(FrameLoadStartEventArgs args)
{
FrameLoadStart?.Invoke(this, args);
}
///
/// Handles the event.
///
/// The instance containing the event data.
internal void OnFrameLoadEnd(FrameLoadEndEventArgs args)
{
FrameLoadEnd?.Invoke(this, args);
}
///
/// Handles the event.
///
/// The instance containing the event data.
internal void OnConsoleMessage(ConsoleMessageEventArgs args)
{
ConsoleMessage?.Invoke(this, args);
}
///
/// Handles the event.
///
/// The instance containing the event data.
internal void OnStatusMessage(StatusMessageEventArgs args)
{
StatusMessage?.Invoke(this, args);
}
///
/// Handles the event.
///
/// The instance containing the event data.
internal void OnLoadError(LoadErrorEventArgs args)
{
LoadError?.Invoke(this, args);
}
///
/// Sets the loading state change.
///
/// The instance containing the event data.
internal void OnLoadingStateChange(LoadingStateChangedEventArgs args)
{
CanGoBack = args.CanGoBack;
CanGoForward = args.CanGoForward;
IsLoading = args.IsLoading;
LoadingStateChanged?.Invoke(this, args);
}
///
/// Sets the title.
///
/// The instance containing the event data.
internal void OnTitleChanged(TitleChangedEventArgs args)
{
TitleChanged?.Invoke(this, args);
}
///
/// Sets the address.
///
/// The instance containing the event data.
internal void OnAddressChanged(AddressChangedEventArgs args)
{
Address = args.Address;
AddressChanged?.Invoke(this, args);
}
///
/// Loads the specified in the Main Frame.
///
/// The URL to be loaded.
public void LoadUrl(string url)
{
if (IsDisposed)
{
return;
}
var browser = BrowserCore;
if (browser == null || browser.IsDisposed)
{
return;
}
using (var frame = browser.MainFrame)
{
frame.LoadUrl(url);
}
}
///
public Task LoadUrlAsync(string url)
{
//LoadUrlAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
return CefSharp.WebBrowserExtensions.LoadUrlAsync(this, url);
}
///
public Task WaitForNavigationAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
//WaitForNavigationAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
return CefSharp.WebBrowserExtensions.WaitForNavigationAsync(this, timeout, cancellationToken);
}
///
/// Returns the main (top-level) frame for the browser window.
///
/// the main frame
public IFrame GetMainFrame()
{
var browser = BrowserCore;
if(browser == null)
{
throw new Exception(CefSharp.WebBrowserExtensions.BrowserNullExceptionString);
}
return browser.MainFrame;
}
///
protected override void Dispose(bool disposing)
{
if (disposing)
{
var browserCore = BrowserCore;
AddressChanged = null;
ConsoleMessage = null;
FrameLoadEnd = null;
FrameLoadStart = null;
LoadError = null;
LoadingStateChanged = null;
StatusMessage = null;
TitleChanged = null;
BrowserCore = null;
if (browserCore?.IsDisposed == false)
{
//Close the underlying CEF Browser
browserCore?.GetHost()?.CloseBrowser(true);
}
}
base.Dispose(disposing);
}
///
/// Gets the associated with
/// a specific instance.
///
/// browser
/// returns the assocaited or null if Disposed or no host found.
public static ChromiumHostControl FromBrowser(IBrowser browser)
{
return FromBrowser(browser);
}
}
}