Add project

This commit is contained in:
Diamond Creeper 2023-02-20 23:24:10 +13:00
commit 7fcb279842
961 changed files with 370491 additions and 0 deletions

View file

@ -0,0 +1,14 @@
// Copyright © 2013 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.
namespace CefSharp.WinForms
{
/// <summary>
/// Initialization settings. Many of these and other settings can also configured
/// using command-line switches.
/// </summary>
public class CefSettings : CefSettingsBase
{
}
}

View file

@ -0,0 +1,838 @@
// Copyright © 2013 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.Runtime.CompilerServices;
using System.Threading;
using System.Windows.Forms;
using CefSharp.Internals;
using CefSharp.Web;
using CefSharp.WinForms.Internals;
namespace CefSharp.WinForms
{
/// <summary>
/// ChromiumWebBrowser is the WinForms web browser control
/// </summary>
/// <seealso cref="System.Windows.Forms.Control" />
/// <seealso cref="CefSharp.WinForms.IWinFormsWebBrowser" />
[Docking(DockingBehavior.AutoDock), DefaultEvent("LoadingStateChanged"), ToolboxBitmap(typeof(ChromiumWebBrowser)),
Description("CefSharp ChromiumWebBrowser - Chromium Embedded Framework .Net wrapper. https://github.com/cefsharp/CefSharp"),
Designer(typeof(ChromiumWebBrowserDesigner))]
public partial class ChromiumWebBrowser : Control, IWebBrowserInternal, IWinFormsWebBrowser
{
//TODO: If we start adding more consts then extract them into a common class
//Possibly in the CefSharp assembly and move the WPF ones into there as well.
private const uint WS_EX_NOACTIVATE = 0x08000000;
/// <summary>
/// The managed cef browser adapter
/// </summary>
private IBrowserAdapter managedCefBrowserAdapter;
/// <summary>
/// The parent form message interceptor
/// </summary>
private ParentFormMessageInterceptor parentFormMessageInterceptor;
/// <summary>
/// The browser
/// </summary>
private IBrowser browser;
/// <summary>
/// A flag that indicates whether or not the designer is active
/// NOTE: DesignMode becomes false by the time we get to the destructor/dispose so it gets stored here
/// </summary>
private bool designMode;
/// <summary>
/// A flag that indicates whether or not <see cref="InitializeFieldsAndCefIfRequired"/> has been called.
/// </summary>
private bool initialized;
/// <summary>
/// Has the underlying Cef Browser been created (slightly different to initialized in that
/// the browser is initialized in an async fashion)
/// </summary>
private bool browserCreated;
/// <summary>
/// A flag indicating if the <see cref="Address"/> was used when calling CreateBrowser
/// If false and <see cref="Address"/> contains a non empty string Load will be called
/// on the main frame
/// </summary>
private bool initialAddressLoaded;
/// <summary>
/// If true the the WS_EX_NOACTIVATE style will be removed so that future mouse clicks
/// inside the browser correctly activate and focus the window.
/// </summary>
private bool removeExNoActivateStyle;
/// <summary>
/// Browser initialization settings
/// </summary>
private IBrowserSettings browserSettings;
/// <summary>
/// The request context (we deliberately use a private variable so we can throw an exception if
/// user attempts to set after browser created)
/// </summary>
private IRequestContext requestContext;
/// <summary>
/// The value for disposal, if it's 1 (one) then this instance is either disposed
/// or in the process of getting disposed
/// </summary>
private int disposeSignaled;
/// <summary>
/// Parking control used to temporarily host the CefBrowser instance
/// when <see cref="RecreatingHandle"/> is <c>true</c>.
/// </summary>
private Control parkingControl;
/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
/// <value><see langword="true" /> if this instance is disposed; otherwise, <see langword="false" />.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public new bool IsDisposed
{
get
{
return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1;
}
}
/// <summary>
/// Set to true while handing an activating WM_ACTIVATE message.
/// MUST ONLY be cleared by DefaultFocusHandler.
/// </summary>
/// <value><c>true</c> if this instance is activating; otherwise, <c>false</c>.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool IsActivating { get; set; }
/// <summary>
/// Gets or sets the browser settings.
/// </summary>
/// <value>The browser settings.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public IBrowserSettings BrowserSettings
{
get
{
//We keep a reference to the browserSettings for the case where
//the Control Handle is destroyed then Created see https://github.com/cefsharp/CefSharp/issues/2840
//As it's not possible to change settings after the browser has been
//created, and changing browserSettings then creating a new handle will
//give a subtle different user experience if you aren't expecting it we
//return null here even though we still have a reference.
if (browserCreated)
{
return null;
}
return browserSettings;
}
set
{
if (browserCreated)
{
throw new Exception("Browser has already been created. BrowserSettings must be " +
"set before the underlying CEF browser is created.");
}
if (value != null && !Core.ObjectFactory.BrowserSetingsType.IsAssignableFrom(value.UnWrap().GetType()))
{
throw new Exception(string.Format("BrowserSettings can only be of type {0} or null", Core.ObjectFactory.BrowserSetingsType));
}
browserSettings = value;
}
}
/// <summary>
/// Activates browser upon creation, the default value is false. Prior to version 73
/// the default behaviour was to activate browser on creation (Equivalent of setting this property to true).
/// To restore this behaviour set this value to true immediately after you create the <see cref="ChromiumWebBrowser"/> instance.
/// https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
/// </summary>
public bool ActivateBrowserOnCreation { get; set; }
/// <summary>
/// Gets or sets the request context.
/// </summary>
/// <value>The request context.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public IRequestContext RequestContext
{
get { return requestContext; }
set
{
if (browserCreated)
{
throw new Exception("Browser has already been created. RequestContext must be " +
"set before the underlying CEF browser is created.");
}
if (value != null && !Core.ObjectFactory.RequestContextType.IsAssignableFrom(value.UnWrap().GetType()))
{
throw new Exception(string.Format("RequestContext can only be of type {0} or null", Core.ObjectFactory.RequestContextType));
}
requestContext = value;
}
}
/// <summary>
/// A flag that indicates whether the control is currently loading one or more web pages (true) or not (false).
/// </summary>
/// <value><c>true</c> if this instance is loading; otherwise, <c>false</c>.</value>
/// <remarks>In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.</remarks>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool IsLoading { get; private set; }
/// <summary>
/// The text that will be displayed as a ToolTip
/// </summary>
/// <value>The tooltip text.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public string TooltipText { get; private set; }
/// <summary>
/// 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).
/// </summary>
/// <value>The address.</value>
/// <remarks>In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.</remarks>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null)]
public string Address { get; private set; }
/// <summary>
/// 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.
/// </summary>
public event EventHandler<AddressChangedEventArgs> AddressChanged;
/// <summary>
/// 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.
/// </summary>
public event EventHandler<TitleChangedEventArgs> TitleChanged;
/// <summary>
/// Event called after the underlying CEF browser instance has been created.
/// 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.
/// </summary>
public event EventHandler IsBrowserInitializedChanged;
/// <summary>
/// A flag that indicates whether the state of the control currently supports the GoForward action (true) or not (false).
/// </summary>
/// <value><c>true</c> if this instance can go forward; otherwise, <c>false</c>.</value>
/// <remarks>In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.</remarks>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool CanGoForward { get; private set; }
/// <summary>
/// A flag that indicates whether the state of the control current supports the GoBack action (true) or not (false).
/// </summary>
/// <value><c>true</c> if this instance can go back; otherwise, <c>false</c>.</value>
/// <remarks>In the WPF control, this property is implemented as a Dependency Property and fully supports data
/// binding.</remarks>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool CanGoBack { get; private set; }
/// <summary>
/// A flag that indicates whether the WebBrowser is initialized (true) or not (false).
/// </summary>
/// <value><c>true</c> if this instance is browser initialized; otherwise, <c>false</c>.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)]
public bool IsBrowserInitialized
{
get { return InternalIsBrowserInitialized(); }
}
/// <summary>
/// ParentFormMessageInterceptor hooks the Form handle and forwards
/// the move/active messages to the browser, the default is true
/// and should only be required when using <see cref="CefSettingsBase.MultiThreadedMessageLoop"/>
/// set to true.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(true)]
public bool UseParentFormMessageInterceptor { get; set; } = true;
/// <summary>
/// By default when <see cref="Control.OnHandleDestroyed(EventArgs)"/> is called
/// the underlying Browser Hwnd is only parked (moved to a temp parent)
/// when <see cref="Control.RecreatingHandle"/> is <c>true</c>, there are a few other
/// cases where parking of the control is desired, you can force parking by setting
/// this property to <c>true</c>.
/// </summary>
/// <remarks>
/// You may wish to set this property to <c>true</c> when using the browser in conjunction
/// with https://github.com/dockpanelsuite/dockpanelsuite
/// </remarks>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(true)]
public bool ParkControlOnHandleDestroyed { get; set; } = false;
/// <summary>
/// Initializes static members of the <see cref="ChromiumWebBrowser"/> class.
/// </summary>
static ChromiumWebBrowser()
{
if (CefSharpSettings.ShutdownOnExit)
{
Application.ApplicationExit += OnApplicationExit;
}
}
/// <summary>
/// Handles the <see cref="E:ApplicationExit" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private static void OnApplicationExit(object sender, EventArgs e)
{
Cef.Shutdown();
}
/// <summary>
/// This constructor exists as the WinForms designer requires a parameterless constructor, if you are instantiating
/// an instance of this class in code then use the <see cref="ChromiumWebBrowser(string, IRequestContext)"/>
/// constructor overload instead. Using this constructor in code is unsupported and you may experience <see cref="NullReferenceException"/>'s
/// when attempting to access some of the properties immediately after instantiation.
/// </summary>
[Obsolete("Should only be used by the WinForms Designer. Use the ChromiumWebBrowser(string, IRequestContext) constructor overload instead.")]
public ChromiumWebBrowser()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ChromiumWebBrowser"/> class.
/// **Important** - When using this constructor the <see cref="Control.Dock"/> property
/// will default to <see cref="DockStyle.Fill"/>.
/// </summary>
/// <param name="html">html string to be initially loaded in the browser.</param>
/// <param name="requestContext">(Optional) Request context that will be used for this browser instance, if null the Global
/// Request Context will be used.</param>
public ChromiumWebBrowser(HtmlString html, IRequestContext requestContext = null) : this(html.ToDataUriString(), requestContext)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ChromiumWebBrowser"/> class.
/// **Important** - When using this constructor the <see cref="Control.Dock"/> property
/// will default to <see cref="DockStyle.Fill"/>.
/// </summary>
/// <param name="address">The address.</param>
/// <param name="requestContext">(Optional) Request context that will be used for this browser instance, if null the Global
/// Request Context will be used.</param>
public ChromiumWebBrowser(string address, IRequestContext requestContext = null)
{
Dock = DockStyle.Fill;
Address = address;
RequestContext = requestContext;
InitializeFieldsAndCefIfRequired();
}
/// <summary>
/// Required for designer support - this method cannot be inlined as the designer
/// will attempt to load libcef.dll and will subsequently throw an exception.
/// TODO: Still not happy with this method name, need something better
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private void InitializeFieldsAndCefIfRequired()
{
if (!initialized)
{
if (!Cef.IsInitialized && !Cef.Initialize(new CefSettings()))
{
throw new InvalidOperationException("Cef::Initialize() failed");
}
Cef.AddDisposable(this);
if (FocusHandler == null)
{
//If the WinForms UI thread and the CEF UI thread are one in the same
//then we don't need the FocusHandler, it's only required when using
//MultiThreadedMessageLoop (the default)
if (!Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
FocusHandler = new DefaultFocusHandler();
}
}
if (browserSettings == null)
{
browserSettings = Core.ObjectFactory.CreateBrowserSettings(autoDispose: true);
}
managedCefBrowserAdapter = ManagedCefBrowserAdapter.Create(this, false);
initialized = true;
}
}
/// <summary>
/// If not in design mode; Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/>
/// </summary>
/// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
// Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that
// this thread is the first thread to do so, and can safely dispose of the object.
if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0)
{
return;
}
if (!designMode)
{
InternalDispose(disposing);
}
base.Dispose(disposing);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/>
/// </summary>
/// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param>
/// <remarks>
/// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsequently throw an exception.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining)]
private void InternalDispose(bool disposing)
{
if (disposing)
{
Interlocked.Exchange(ref browserInitialized, 0);
CanExecuteJavascriptInMainFrame = false;
// Don't maintain a reference to event listeners anylonger:
AddressChanged = null;
ConsoleMessage = null;
FrameLoadEnd = null;
FrameLoadStart = null;
IsBrowserInitializedChanged = null;
LoadError = null;
LoadingStateChanged = null;
StatusMessage = null;
TitleChanged = null;
JavascriptMessageReceived = null;
// Release reference to handlers, except LifeSpanHandler which is done after Disposing
// ManagedCefBrowserAdapter otherwise the ILifeSpanHandler.DoClose will not be invoked.
this.SetHandlersToNullExceptLifeSpan();
browser = null;
if (parentFormMessageInterceptor != null)
{
parentFormMessageInterceptor.Dispose();
parentFormMessageInterceptor = null;
}
if (managedCefBrowserAdapter != null)
{
managedCefBrowserAdapter.Dispose();
managedCefBrowserAdapter = null;
}
//Dispose of BrowserSettings if we created it, if user created then they're responsible
if (browserSettings != null && browserSettings.AutoDispose)
{
browserSettings.Dispose();
}
browserSettings = null;
parkingControl?.Dispose();
parkingControl = null;
// LifeSpanHandler is set to null after managedCefBrowserAdapter.Dispose so ILifeSpanHandler.DoClose
// is called.
LifeSpanHandler = null;
}
Cef.RemoveDisposable(this);
}
/// <summary>
/// Loads the specified URL.
/// </summary>
/// <param name="url">The URL to be loaded.</param>
public void Load(string url)
{
if (IsBrowserInitialized)
{
using (var frame = this.GetMainFrame())
{
frame.LoadUrl(url);
}
}
else
{
Address = url;
}
}
/// <summary>
/// The javascript object repository, one repository per ChromiumWebBrowser instance.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IJavascriptObjectRepository JavascriptObjectRepository
{
get
{
InitializeFieldsAndCefIfRequired();
return managedCefBrowserAdapter == null ? null : managedCefBrowserAdapter.JavascriptObjectRepository;
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.HandleCreated" /> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
protected override void OnHandleCreated(EventArgs e)
{
designMode = DesignMode;
if (!designMode)
{
InitializeFieldsAndCefIfRequired();
// NOTE: Had to move the code out of this function otherwise the designer would crash
CreateBrowser();
ResizeBrowser(Width, Height);
}
base.OnHandleCreated(e);
}
protected override void OnHandleDestroyed(EventArgs e)
{
if (!designMode)
{
// NOTE: Had to move the code out of this function otherwise the designer would crash
OnHandleDestroyedInternal();
}
base.OnHandleDestroyed(e);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void OnHandleDestroyedInternal()
{
//When the Control is being Recreated then we'll park
//the browser (set to a temp parent) and assign to
//our new handle when it's ready.
if (RecreatingHandle || ParkControlOnHandleDestroyed)
{
parkingControl = new Control();
parkingControl.CreateControl();
var host = this.GetBrowserHost();
var hwnd = host.GetWindowHandle();
NativeMethodWrapper.SetWindowParent(hwnd, parkingControl.Handle);
}
}
/// <summary>
/// Override this method to handle creation of WindowInfo. This method can be used to customise aspects of
/// browser creation including configuration of settings such as <see cref="IWindowInfo.ExStyle"/>.
/// Window Activation is disabled by default, you can re-enable it by overriding and removing the
/// WS_EX_NOACTIVATE style from <see cref="IWindowInfo.ExStyle"/>.
/// </summary>
/// <param name="handle">Window handle for the Control</param>
/// <returns>Window Info</returns>
/// <example>
/// To re-enable Window Activation then remove WS_EX_NOACTIVATE from ExStyle
/// <code>
/// const uint WS_EX_NOACTIVATE = 0x08000000;
/// windowInfo.ExStyle &amp;= ~WS_EX_NOACTIVATE;
///</code>
/// </example>
protected virtual IWindowInfo CreateBrowserWindowInfo(IntPtr handle)
{
var windowInfo = Core.ObjectFactory.CreateWindowInfo();
windowInfo.SetAsChild(handle);
if (!ActivateBrowserOnCreation)
{
//Disable Window activation by default
//https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
windowInfo.ExStyle |= WS_EX_NOACTIVATE;
}
return windowInfo;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void CreateBrowser()
{
browserCreated = true;
if (((IWebBrowserInternal)this).HasParent == false)
{
//If we are Recreating our handle we will have re-parented our
//browser to parkingControl. We'll assign the browser to our newly
//created handle now.
if ((RecreatingHandle || ParkControlOnHandleDestroyed) && IsBrowserInitialized && browser != null)
{
var host = this.GetBrowserHost();
var hwnd = host.GetWindowHandle();
NativeMethodWrapper.SetWindowParent(hwnd, Handle);
parkingControl.Dispose();
parkingControl = null;
}
else
{
var windowInfo = CreateBrowserWindowInfo(Handle);
//We actually check if WS_EX_NOACTIVATE was set for instances
//the user has override CreateBrowserWindowInfo and not called base.CreateBrowserWindowInfo
removeExNoActivateStyle = (windowInfo.ExStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE;
initialAddressLoaded = !string.IsNullOrEmpty(Address);
managedCefBrowserAdapter.CreateBrowser(windowInfo, browserSettings, requestContext, Address);
}
}
}
/// <summary>
/// Called after browser created.
/// </summary>
/// <param name="browser">The browser.</param>
void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser)
{
this.browser = browser;
Interlocked.Exchange(ref browserInitialized, 1);
// By the time this callback gets called, this control
// is most likely hooked into a browser Form of some sort.
// (Which is what ParentFormMessageInterceptor relies on.)
// Ensure the ParentFormMessageInterceptor construction occurs on the WinForms UI thread:
if (UseParentFormMessageInterceptor)
{
this.InvokeOnUiThreadIfRequired(() =>
{
parentFormMessageInterceptor = new ParentFormMessageInterceptor(this);
});
}
ResizeBrowser(Width, Height);
//If Load was called after the call to CreateBrowser we'll call Load
//on the MainFrame
if (!initialAddressLoaded && !string.IsNullOrEmpty(Address))
{
browser.MainFrame.LoadUrl(Address);
}
IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Sets the address.
/// </summary>
/// <param name="args">The <see cref="AddressChangedEventArgs"/> instance containing the event data.</param>
void IWebBrowserInternal.SetAddress(AddressChangedEventArgs args)
{
Address = args.Address;
var handler = AddressChanged;
if (handler != null)
{
handler(this, args);
}
}
/// <summary>
/// Sets the loading state change.
/// </summary>
/// <param name="args">The <see cref="LoadingStateChangedEventArgs"/> instance containing the event data.</param>
void IWebBrowserInternal.SetLoadingStateChange(LoadingStateChangedEventArgs args)
{
CanGoBack = args.CanGoBack;
CanGoForward = args.CanGoForward;
IsLoading = args.IsLoading;
if (removeExNoActivateStyle && browser != null)
{
removeExNoActivateStyle = false;
var host = this.GetBrowserHost();
var hwnd = host.GetWindowHandle();
//Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the
//browser correctly activate and focus the browser.
//https://github.com/chromiumembedded/cef/blob/9df4a54308a88fd80c5774d91c62da35afb5fd1b/tests/cefclient/browser/root_window_win.cc#L1088
NativeMethodWrapper.RemoveExNoActivateStyle(hwnd);
}
var handler = LoadingStateChanged;
if (handler != null)
{
handler(this, args);
}
}
/// <summary>
/// Sets the title.
/// </summary>
/// <param name="args">The <see cref="TitleChangedEventArgs"/> instance containing the event data.</param>
void IWebBrowserInternal.SetTitle(TitleChangedEventArgs args)
{
var handler = TitleChanged;
if (handler != null)
{
handler(this, args);
}
}
/// <summary>
/// Sets the tooltip text.
/// </summary>
/// <param name="tooltipText">The tooltip text.</param>
void IWebBrowserInternal.SetTooltipText(string tooltipText)
{
TooltipText = tooltipText;
}
/// <summary>
/// Manually implement Focused because cef does not implement it.
/// </summary>
/// <value><c>true</c> if focused; otherwise, <c>false</c>.</value>
/// <remarks>This is also how the Microsoft's WebBrowserControl implements the Focused property.</remarks>
public override bool Focused
{
get
{
if (base.Focused)
{
return true;
}
if (!IsHandleCreated)
{
return false;
}
return NativeMethodWrapper.IsFocused(Handle);
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.SizeChanged" /> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (!designMode && initialized)
{
ResizeBrowser(Width, Height);
}
}
/// <summary>
/// Resizes the browser.
/// </summary>
private void ResizeBrowser(int width, int height)
{
if (IsBrowserInitialized)
{
managedCefBrowserAdapter.Resize(width, height);
}
}
/// <summary>
/// When minimized set the browser window size to 0x0 to reduce resource usage.
/// https://github.com/chromiumembedded/cef/blob/c7701b8a6168f105f2c2d6b239ce3958da3e3f13/tests/cefclient/browser/browser_window_std_win.cc#L87
/// </summary>
internal void HideInternal()
{
ResizeBrowser(0, 0);
}
/// <summary>
/// Show the browser (called after previous minimised)
/// </summary>
internal void ShowInternal()
{
ResizeBrowser(Width, Height);
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.GotFocus" /> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
protected override void OnGotFocus(EventArgs e)
{
if (IsBrowserInitialized)
{
browser.GetHost().SetFocus(true);
}
base.OnGotFocus(e);
}
/// <summary>
/// Returns the current IBrowser Instance
/// </summary>
/// <returns>browser instance or null</returns>
public IBrowser GetBrowser()
{
ThrowExceptionIfDisposed();
ThrowExceptionIfBrowserNotInitialized();
return browser;
}
/// <summary>
/// Gets the default size of the control.
/// </summary>
/// <value>
/// The default <see cref="T:System.Drawing.Size" /> of the control.
/// </value>
protected override Size DefaultSize
{
get { return new Size(200, 100); }
}
/// <summary>
/// Makes certain keys as Input keys when CefSettings.MultiThreadedMessageLoop = false
/// </summary>
/// <param name="keyData">key data</param>
/// <returns>true for a select list of keys otherwise defers to base.IsInputKey</returns>
protected override bool IsInputKey(Keys keyData)
{
//This code block is only called/required when CEF is running in the
//same message loop as the WinForms UI (CefSettings.MultiThreadedMessageLoop = false)
//Without this code, arrows and tab won't be processed
switch (keyData)
{
case Keys.Right:
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Tab:
{
return true;
}
case Keys.Shift | Keys.Tab:
case Keys.Shift | Keys.Right:
case Keys.Shift | Keys.Left:
case Keys.Shift | Keys.Up:
case Keys.Shift | Keys.Down:
{
return true;
}
}
return base.IsInputKey(keyData);
}
}
}

View file

@ -0,0 +1,67 @@
// Copyright © 2018 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.Collections;
using System.Drawing;
using System.Windows.Forms.Design;
namespace CefSharp.WinForms
{
/// <summary>
/// ChromiumWebBrowser Control Designer
/// </summary>
public class ChromiumWebBrowserDesigner : ControlDesigner
{
/// <summary>
/// Receives a call when the control that the designer is managing has painted its surface so the designer can paint any additional adornments on top of the control.
/// </summary>
/// <param name="pe">args</param>
protected override void OnPaintAdornments(System.Windows.Forms.PaintEventArgs pe)
{
//NOTE: Removed until a better image can be found, add image as Embedded Resource and update name below
//var assembly = Assembly.GetAssembly(typeof(ChromiumWebBrowserDesigner));
//using (var logo = assembly.GetManifestResourceStream("CefSharp.WinForms.CefSharpLogo.png"))
//using (var img = Image.FromStream(logo))
//{
// pe.Graphics.DrawImage(img, 0, 0);
//}
using (var font = new Font("Arial", 16))
using (var stringFormat = new StringFormat
{
// Create a StringFormat object with the each line of text, and the block
// of text centered on the page.
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
})
{
pe.Graphics.DrawString("ChromiumWebBrowser", font, Brushes.Black, pe.ClipRectangle, stringFormat);
}
base.OnPaintAdornments(pe);
}
/// <summary>
/// Adjusts the set of properties the component exposes through a TypeDescriptor.
/// </summary>
/// <param name="properties">properties</param>
protected override void PreFilterProperties(IDictionary properties)
{
//Remove some of the default properties from the designer
//they don't have much meaning for the browser
//Probably more that can be removed/tweaked
properties.Remove("BackgroundImage");
properties.Remove("BackgroundImageLayout");
properties.Remove("Text");
properties.Remove("Font");
properties.Remove("ForeColor");
properties.Remove("BackColor");
properties.Remove("RightToLeft");
base.PreFilterProperties(properties);
}
}
}

View file

@ -0,0 +1,31 @@
// Copyright © 2014 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;
namespace CefSharp.WinForms
{
/// <summary>
/// WinForms specific implementation, has events the
/// <see cref="ChromiumWebBrowser" /> implementation exposes.
/// </summary>
/// <seealso cref="CefSharp.IWebBrowser" />
public interface IWinFormsWebBrowser : IWebBrowser
{
/// <summary>
/// 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.
/// </summary>
event EventHandler<TitleChangedEventArgs> TitleChanged;
/// <summary>
/// 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.
/// </summary>
event EventHandler<AddressChangedEventArgs> AddressChanged;
}
}

View file

@ -0,0 +1,121 @@
// Copyright © 2015 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;
namespace CefSharp.WinForms.Internals
{
/// <summary>
/// ControlExtensions.
/// </summary>
public static class ControlExtensions
{
/// <summary>
/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
/// No action will be performed if the control doesn't have a valid handle or the control is Disposed/Disposing.
/// </summary>
/// <param name="control">the control for which the update is required</param>
/// <param name="action">action to be performed on the control</param>
internal static void InvokeOnUiThreadIfRequired(this Control control, Action action)
{
//No action
if (control.Disposing || control.IsDisposed || !control.IsHandleCreated)
{
return;
}
if (control.InvokeRequired)
{
control.BeginInvoke((Action)(() =>
{
//No action
if (control.Disposing || control.IsDisposed || !control.IsHandleCreated)
{
return;
}
action();
}));
}
else
{
action.Invoke();
}
}
/// <summary>
/// Activates the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
public static bool Activate(this Control control)
{
// Notify WinForms world that inner browser window got focus. This will trigger Leave event to previous focused control
var containerControl = control.GetContainerControl();
if (containerControl != null)
{
return containerControl.ActivateControl(control);
}
return false;
}
/// <summary>
/// Returns whether the supplied control is the currently
/// active control.
/// </summary>
/// <param name="control">the control to check</param>
/// <returns>true if the control is the currently active control</returns>
public static bool IsActiveControl(this Control control)
{
Form form = control.FindForm();
if (form == null)
{
return false;
}
Control activeControl = form.ActiveControl;
while (activeControl != null
&& (activeControl is ContainerControl)
&& !Object.ReferenceEquals(control, activeControl))
{
var containerControl = activeControl as ContainerControl;
activeControl = containerControl.ActiveControl;
}
return Object.ReferenceEquals(control, activeControl);
}
/// <summary>
/// Selects the next control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="next">if set to <c>true</c> [next].</param>
public static void SelectNextControl(this Control control, bool next)
{
var containerControl = control.GetContainerControl();
while (containerControl != null)
{
var containerControlAsControl = containerControl as Control;
if (containerControlAsControl == null)
{
break;
}
var activeControl = containerControl.ActiveControl;
if (containerControlAsControl.SelectNextControl(activeControl, next, true, true, false))
{
break;
}
if (containerControlAsControl.Parent == null)
{
break;
}
containerControl = containerControlAsControl.Parent.GetContainerControl();
}
}
}
}

View file

@ -0,0 +1,122 @@
// Copyright © 2014 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.
namespace CefSharp.WinForms.Internals
{
/// <summary>
/// Default implementation of <see cref="IFocusHandler" />
/// for the WinForms implementation
/// </summary>
/// <seealso cref="CefSharp.IFocusHandler" />
public class DefaultFocusHandler : IFocusHandler
{
/// <summary>
/// Called when the browser component has received focus.
/// </summary>
/// <param name="chromiumWebBrowser">the ChromiumWebBrowser control</param>
/// <param name="browser">the browser object</param>
/// <remarks>Try to avoid needing to override this logic in a subclass. The implementation in
/// DefaultFocusHandler relies on very detailed behavior of how WinForms and
/// Windows interact during window activation.</remarks>
public virtual void OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
//We don't deal with popups as they're rendered by default entirely by CEF
//For print dialogs the browser will be null, we don't want to deal with that either.
if (browser == null || browser.IsPopup)
{
return;
}
var winFormsChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
// During application activation, CEF receives a WM_SETFOCUS
// message from Windows because it is the top window
// on the CEF UI thread.
//
// If the WinForm ChromiumWebBrowser control is the
// current .ActiveControl before app activation
// then we MUST NOT try to reactivate the WinForm
// control during activation because that will
// start a race condition between reactivating
// the CEF control AND having another control
// that should be the new .ActiveControl.
//
// For example:
// * CEF control has focus, and thus ChromiumWebBrowser
// is the current .ActiveControl
// * Alt-Tab to another application
// * Click a non CEF control in the WinForms application.
// * This begins the Windows activation process.
// * The WM_ACTIVATE process on the WinForm UI thread
// will update .ActiveControl to the clicked control.
// The clicked control will receive WM_SETFOCUS as well.
// (i.e. OnGotFocus)
// If the ChromiumWebBrowser was the previous .ActiveControl,
// then we set .Activating = true.
// * The WM_ACTIVATE process on the CEF thread will
// send WM_SETFOCUS to CEF thus staring the race of
// which will end first, the WndProc WM_ACTIVATE process
// on the WinForm UI thread or the WM_ACTIVATE process
// on the CEF UI thread.
// * CEF will then call this method on the CEF UI thread
// due to WM_SETFOCUS.
// * This method will clear the activation state (if any)
// on the ChromiumWebBrowser control, due to the race
// condition the WinForm UI thread cannot.
if (winFormsChromiumWebBrowser.IsActivating)
{
winFormsChromiumWebBrowser.IsActivating = false;
}
else
{
// Otherwise, we're not being activated
// so we must activate the ChromiumWebBrowser control
// for WinForms focus tracking.
winFormsChromiumWebBrowser.InvokeOnUiThreadIfRequired(() =>
{
winFormsChromiumWebBrowser.Activate();
});
}
}
/// <summary>
/// Called when the browser component is requesting focus.
/// </summary>
/// <param name="chromiumWebBrowser">the ChromiumWebBrowser control</param>
/// <param name="browser">the browser object</param>
/// <param name="source">Indicates where the focus request is originating from.</param>
/// <returns>Return false to allow the focus to be set or true to cancel setting the focus.</returns>
public virtual bool OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source)
{
//We don't deal with popups as they're rendered by default entirely by CEF
if (browser.IsPopup)
{
return false;
}
// Do not let the browser take focus when a Load method has been called
return source == CefFocusSource.FocusSourceNavigation;
}
/// <summary>
/// Called when the browser component is about to lose focus.
/// For instance, if focus was on the last HTML element and the user pressed the TAB key.
/// </summary>
/// <param name="chromiumWebBrowser">the ChromiumWebBrowser control</param>
/// <param name="browser">the browser object</param>
/// <param name="next">Will be true if the browser is giving focus to the next component
/// and false if the browser is giving focus to the previous component.</param>
public virtual void OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next)
{
//We don't deal with popups as they're rendered by default entirely by CEF
if (browser.IsPopup)
{
return;
}
var winFormsChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
// NOTE: OnTakeFocus means leaving focus / not taking focus
winFormsChromiumWebBrowser.InvokeOnUiThreadIfRequired(() => winFormsChromiumWebBrowser.SelectNextControl(next));
}
}
}

View file

@ -0,0 +1,25 @@
// Copyright © 2015 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.
namespace CefSharp.WinForms.Internals
{
/// <summary>
/// Class NativeMethods.
/// </summary>
internal static class NativeMethods
{
/// <summary>
/// The w m_ move
/// </summary>
public const int WM_MOVE = 0x3;
/// <summary>
/// The w m_ moving
/// </summary>
public const int WM_MOVING = 0x216;
/// <summary>
/// The w m_ activate
/// </summary>
public const int WM_ACTIVATE = 0x6;
}
}

View file

@ -0,0 +1,345 @@
// Copyright © 2015 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.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using CefSharp.Internals;
namespace CefSharp.WinForms.Internals
{
/// <summary>
/// ParentFormMessageInterceptor - hooks into the parent forms
/// message loop to incercept messages like WM_MOVE
/// </summary>
/// <seealso cref="System.Windows.Forms.NativeWindow" />
/// <seealso cref="System.IDisposable" />
internal class ParentFormMessageInterceptor : NativeWindow, IDisposable
{
/// <summary>
/// Keep track of whether a move is in progress.
/// </summary>
private bool isMoving;
/// <summary>
/// Used to determine the coordinates involved in the move
/// </summary>
private Rectangle movingRectangle;
/// <summary>
/// Store the previous window state, used to determine if the
/// Windows was previously <see cref="FormWindowState.Minimized"/>
/// and resume rendering
/// </summary>
private FormWindowState previousWindowState;
/// <summary>
/// Gets or sets the browser.
/// </summary>
/// <value>The browser.</value>
private ChromiumWebBrowser Browser { get; set; }
/// <summary>
/// Gets or sets the parent form.
/// </summary>
/// <value>The parent form.</value>
private Form ParentForm { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ParentFormMessageInterceptor"/> class.
/// </summary>
/// <param name="browser">The browser.</param>
public ParentFormMessageInterceptor(ChromiumWebBrowser browser)
{
Browser = browser;
// Get notified if our browser window parent changes:
Browser.ParentChanged += ParentParentChanged;
// Find the browser form to subclass to monitor WM_MOVE/WM_MOVING
RefindParentForm();
}
/// <summary>
/// Call to force refinding of the parent Form.
/// (i.e. top level window that owns the ChromiumWebBrowserControl)
/// </summary>
public void RefindParentForm()
{
ParentParentChanged(Browser, null);
}
/// <summary>
/// Adjust the form to listen to if the ChromiumWebBrowserControl's parent changes.
/// </summary>
/// <param name="sender">The ChromiumWebBrowser whose parent has changed.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
private void ParentParentChanged(object sender, EventArgs e)
{
var control = (Control)sender;
var oldForm = ParentForm;
var newForm = control.FindForm();
if (oldForm == null || newForm == null || oldForm.Handle != newForm.Handle)
{
if (Handle != IntPtr.Zero)
{
ReleaseHandle();
}
if (oldForm != null)
{
oldForm.HandleCreated -= OnHandleCreated;
oldForm.HandleDestroyed -= OnHandleDestroyed;
oldForm.Resize -= OnResize;
}
ParentForm = newForm;
if (newForm != null)
{
newForm.HandleCreated += OnHandleCreated;
newForm.HandleDestroyed += OnHandleDestroyed;
newForm.Resize += OnResize;
previousWindowState = newForm.WindowState;
// If newForm's Handle has been created already,
// our event listener won't be called, so call it now.
if (newForm.IsHandleCreated)
{
OnHandleCreated(newForm, null);
}
}
}
}
private void OnResize(object sender, EventArgs e)
{
var form = (Form)sender;
if (previousWindowState == form.WindowState)
{
return;
}
switch (form.WindowState)
{
case FormWindowState.Normal:
case FormWindowState.Maximized:
{
if (previousWindowState == FormWindowState.Minimized)
{
Browser?.ShowInternal();
}
break;
}
case FormWindowState.Minimized:
{
Browser?.HideInternal();
break;
}
}
previousWindowState = form.WindowState;
}
/// <summary>
/// Handles the <see cref="E:HandleCreated" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnHandleCreated(object sender, EventArgs e)
{
AssignHandle(((Form)sender).Handle);
}
/// <summary>
/// Handles the <see cref="E:HandleDestroyed" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnHandleDestroyed(object sender, EventArgs e)
{
ReleaseHandle();
}
/// <summary>
/// Invokes the default window procedure associated with this window.
/// </summary>
/// <param name="m">A <see cref="T:System.Windows.Forms.Message" /> that is associated with the current Windows message.</param>
protected override void WndProc(ref Message m)
{
var isMovingMessage = false;
// Negative initial values keeps the compiler quiet and to
// ensure we have actual window movement to notify CEF about.
const int invalidMoveCoordinate = -1;
var x = invalidMoveCoordinate;
var y = invalidMoveCoordinate;
// Listen for operating system messages
switch (m.Msg)
{
case NativeMethods.WM_ACTIVATE:
{
// Intercept (de)activate messages for our form so that we can
// ensure that we play nicely with WinForms .ActiveControl
// tracking.
var browser = Browser;
if ((int)m.WParam == 0x0) // WA_INACTIVE
{
// If the CEF browser no longer has focus,
// we won't get a call to OnLostFocus on ChromiumWebBrowser.
// However, it doesn't matter so much since the CEF
// browser will receive it instead.
// Paranoia about making sure the IsActivating state is correct now.
browser.IsActivating = false;
DefWndProc(ref m);
}
else // WA_ACTIVE or WA_CLICKACTIVE
{
// Only set IsActivating if the ChromiumWebBrowser was the form's
// ActiveControl before the last deactivation.
browser.IsActivating = browser.IsActiveControl();
DefWndProc(ref m);
// During activation, WM_SETFOCUS gets sent to
// to the CEF control since it's the root window
// of the CEF UI thread.
//
// Therefore, don't set .IsActivating to false here
// instead do so in DefaultFocusHandler.OnGotFocus.
// Otherwise there's a race condition between this
// thread setting activating to false and
// the CEF DefaultFocusHandler executing to determine
// it shouldn't Activate() the control.
}
return;
}
case NativeMethods.WM_MOVING:
{
movingRectangle = (Rectangle)Marshal.PtrToStructure(m.LParam, typeof(Rectangle));
x = movingRectangle.Left;
y = movingRectangle.Top;
isMovingMessage = true;
break;
}
case NativeMethods.WM_MOVE:
{
// Convert IntPtr into 32bit int safely without
// exceptions:
int dwLParam = m.LParam.CastToInt32();
// Extract coordinates from lo/hi word:
x = dwLParam & 0xffff;
y = (dwLParam >> 16) & 0xffff;
isMovingMessage = true;
break;
}
}
// Only notify about movement if:
// * Browser Handle Created
// NOTE: This is checked for paranoia.
// This WndProc can't be called unless ParentForm has
// its handle created, but that doesn't necessarily mean
// Browser has had its handle created.
// WinForm controls don't usually get eagerly created Handles
// in their constructors.
// * ParentForm Actually moved
// * Not currently moving (on the UI thread only of course)
// * The current WindowState is Normal.
// This check is to simplify the effort here.
// Other logic already handles the maximize/minimize
// cases just fine.
// You might consider checking Browser.Visible and
// not notifying our browser control if the browser control isn't visible.
// However, if you do that, the non-Visible CEF tab will still
// have any SELECT drop downs rendering where they shouldn't.
if (isMovingMessage
&& Browser.IsHandleCreated
&& ParentForm.WindowState == FormWindowState.Normal
&& (ParentForm.Left != x || ParentForm.Top != y)
&& !isMoving)
{
// ParentForm.Left & .Right are negative when the window
// is transitioning from maximized to normal.
// If we are transitioning, the form will also receive
// a WM_SIZE which can deal with the move/size combo itself.
if (ParentForm.Left >= 0 && ParentForm.Right >= 0)
{
OnMoving();
}
}
DefWndProc(ref m);
}
/// <summary>
/// Called when [moving].
/// </summary>
protected virtual void OnMoving()
{
isMoving = true;
if (Browser.IsBrowserInitialized)
{
Browser.GetBrowser().GetHost().NotifyMoveOrResizeStarted();
}
isMoving = false;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (ParentForm != null)
{
ParentForm.HandleCreated -= OnHandleCreated;
ParentForm.HandleDestroyed -= OnHandleDestroyed;
ParentForm.Resize -= OnResize;
ParentForm = null;
}
// Unmanaged resource, but release here anyway.
// NativeWindow has its own finalization logic
// that should be run if this instance isn't disposed
// properly before arriving at the finalization thread.
// See: http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,147
// for the gruesome details.
if (Handle != IntPtr.Zero)
{
ReleaseHandle();
}
if (Browser != null)
{
Browser.ParentChanged -= ParentParentChanged;
Browser = null;
}
}
}
/// <summary>
/// When overridden in a derived class, manages an unhandled thread exception.
/// </summary>
/// <param name="e">An <see cref="T:System.Exception" /> that specifies the unhandled thread exception.</param>
protected override void OnThreadException(Exception e)
{
// TODO: Do something more interesting here, logging, whatever, something.
base.OnThreadException(e);
}
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using CefSharp;
[assembly: AssemblyTitle("CefSharp.WinForm")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyCompany(AssemblyInfo.AssemblyCompany)]
[assembly: AssemblyProduct(AssemblyInfo.AssemblyProduct)]
[assembly: AssemblyCopyright(AssemblyInfo.AssemblyCopyright)]
[assembly: ComVisible(AssemblyInfo.ComVisible)]
[assembly: AssemblyVersion(AssemblyInfo.AssemblyVersion)]
[assembly: AssemblyFileVersion(AssemblyInfo.AssemblyFileVersion)]
[assembly: CLSCompliant(AssemblyInfo.ClsCompliant)]

View file

@ -0,0 +1,61 @@
// Copyright © 2019 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;
namespace CefSharp.WinForms
{
/// <summary>
/// Helper extensions for performing common CefSharp related WinForms tasks
/// </summary>
public static class WebBrowserExtensions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool DestroyWindow(IntPtr hWnd);
/// <summary>
/// Manually call https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow
/// passing in the handle returned from <see cref="IBrowserHost.GetWindowHandle"/>.
/// This method can be used to manually close the underlying CefBrowser instance.
/// This will avoid the WM_Close message that CEF sends by default to the top level window.
/// (Which closes your application). This method should generally only be used in the WinForms version.
/// </summary>
/// <param name="chromiumWebBrowser">the ChromiumWebBrowser instance</param>
/// <returns>If the function succeeds, the return value is true.</returns>
/// <example>
/// <code>
/// //Invoke on the CEF UI Thread
/// Cef.UIThreadTaskFactory.StartNew(() =>
/// {
/// var closed = chromiumWebBrowser.DestroyWindow();
/// });
/// </code>
/// </example>
public static bool DestroyWindow(this IWebBrowser chromiumWebBrowser)
{
if (!Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
throw new InvalidOperationException("This method can only be called on the CEF UI thread." +
"Use Cef.UIThreadTaskFactory to marshal your call onto the CEF UI Thread.");
}
if (chromiumWebBrowser.IsDisposed)
{
return false;
}
var browser = chromiumWebBrowser.GetBrowser();
if (browser == null)
{
return false;
}
var handle = browser.GetHost().GetWindowHandle();
return DestroyWindow(handle);
}
}
}