Skip to content
Alex Maitland edited this page Apr 12, 2022 · 2 revisions

This guide introduces the general concepts involved when developing an application using `CefSharp`. It's important to remember that **`CefSharp` is a .Net wrapper around the [Chromium](https://bitbucket.org/chromiumembedded/cef/overview)**. CEF is an open source project based on the Google Chromium project. Unlike the Chromium project itself, which focuses mainly on Google Chrome application development, CEF focuses on facilitating embedded browser use cases in third-party applications. `CEF` is based on the multi-process Chromium Content API and as a result only a subset of the features that exist in `Chromium` are currently available. For example, support for extensions is limited, only a subset of the `Extension API` is implemented.

The `CEF` project has its own [website](https://bitbucket.org/chromiumembedded/cef/overview) and [Support](http://magpcss.org/ceforum/index.php) if you're dealing with a low level problem or your question is pretty generic you can search or post on the forum. Be sure to mention which `CEF` version you're using. `CefSharp` versions look like `73.1.130`, which maps to `CEF 73.1.13+g6e3c989+chromium-73.0.3683.75`. Open `chrome://version/` or check your `packages.config` to easily determine the version. Post on `ceforum` before opening an issue on the `CEF Issue Tracker`.

It's **important** to remember that `CefSharp` is limited by the `API` that `CEF` exposes, and even then not all of the `CEF API` is currently implemented. If you are chasing a particular feature then check through the [CEF](https://github.com/chromiumembedded/cef/tree/master/include). The [CEF](http://magpcss.org/ceforum/apidocs3/index-all.html) is a helpful resource. If there is a piece of the `CEF API` that is not currently exposed, then you can implement it yourself and submit a `PR` for inclusion in the main project.

This document is based on [CEF](https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md)

`CefSharp` provides four different implementations, `WinForms`, `WPF`, `WPF.HwndHost` and `OffScreen`. The `WPF` and `OffScreen` versions use the `OffScreen Rendering(OSR)` rendering mode. In `OSR` mode each frame is rendered to a buffer and then either drawn on the screen as in the case of `WPF` or available as a `Bitmap` in `OffScreen`. Performance of the `WPF` version is slower than the `WinForms` version. `Wpf.HwndHost` implementation is equivilent ot hosting the `WinForms` version in `WPF` with support for `WPF Data Binding`.

All versions use the `CefSharp`, `CefSharp.Core` and `CefSharp.Core.Runtime` libraries, as a result much of the `API` is used exactly the same within all three flavors. This limits code duplication and reduces the maintenance burden of adding a new feature.

    1. Table of Contents
- [Release](#release-notes) - [Software](#software-requirements)
  * [AnyCPU Support](#anycpu-support)

- [Need](#need-to-knowlimitations) - [Examples](#examples) - [Logging](#logging) - [Processes](#processes) - [Threads](#threads) - [Initialize](#initialize-and-shutdown) - [CefSettings](#cefsettings-and-browsersettings) - [IBrowser,](#ibrowser-iframe-and-ibrowserhost) - [Handlers](#handlers)

  * [Resource Handling](#resource-handling)
  * [Scheme Handler](#scheme-handler)
  * [Request Interception](#request-interception)
  * [ResourceHandler](#resourcehandler)
  * [Response Filtering](#response-filtering)
  * [Loading Html/css/javascript/etc from disk/database/embedded resource/stream](#loading-htmlcssjavascriptetc-from-diskdatabaseembedded-resourcestream)
  * [Request Handling](#request-handling)

- [File](#file-uri-file) - [Proxy](#proxy-resolution) - [Request](#request-context-browser-isolation) - [Printing](#printing) - [High](#high-dpi-displayssupport)

  * [WinForms](#winforms-high-dpi)
  * [WPF](#wpf-high-dpi)
  * [OffScreen](#offscreen-high-dpi)

- [MultiThreadedMessageLoop](#multithreadedmessageloop) - [Popups](#popups) - [JavaScript](#javascript-integration)

  * [1. How do you call a Javascript method from .NET?](#1-how-do-you-call-a-javascript-method-from-net)
    + [When can I start executing `Javascript`?](#when-can-i-start-executing-javascript)
  * [2. How do you call a Javascript method that return a result?](#2-how-do-you-call-a-javascript-method-that-returns-a-result)
  * [3. How do you expose a .NET class to Javascript?](#3-how-do-you-expose-a-net-class-to-javascript)
    + [Async JavaScript Binding (JSB)](#async-javascript-binding-jsb)
    + [Sync JavaScript Binding (JSB)](#sync-javascript-binding-jsb)
    + [RegisterAsyncJsObject](#registerasyncjsobject)
    + [RegisterJsObject](#registerjsobject)
 - [Adobe Flash Player (Pepper Flash)](#adobe-flash-player-pepper-flash)
 - [Offscreen Rendering (OSR)](#offscreen-rendering-osr)
 - [UserAgent](#useragent)
 - [DevTools](#devtools)
 - [Screenshots](#screenshots)
 - [Win32 Out of Memory](#win32-out-of-memory)
 - [Load URL with PostData](#load-url-with-postdata)
 - [Spellchecking](#spellchecking)
 - [WebAssembly](#webassembly)
 - [Exception Handling](#exception-handling)
 - [Dependency Checking](#dependency-checking)
 - [Multimedia (Audio/Video)](#multimedia-audiovideo)
 - [OnScreen (Virtual) Keyboard](#onscreen-virtual-keyboard)
 - [Cookie Manager](#cookie-manager)
    1. Release Notes
Release notes are available for each version at https://github.com/cefsharp/CefSharp/releases please take the time to read over them if you're having problems or curious about what's changed. Check the **Known Issues** section if you're having problems and there are usually notes that contain useful info about a release.
    1. Software Requirements
`CefSharp` uses Visual C++(`VC++`) to interface with the underlying native C++ API, as a result it will only run on Windows. (There is no Windows APP Store version). Each `CefSharp` release has it's own branch, see https://github.com/cefsharp/CefSharp#release-branches for details and requirements for each branch are listed there.
    • CefSharp requires**:
- Microsoft `.Net 4.5.2` or greater - [Microsoft](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) (either `x86` or `x64` depending on your application). To determine which versions of `Visual C++` you require see https://github.com/cefsharp/CefSharp#release-branches

Notes: - You can package the `VC++ Redist Dll's` with your application see https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions#Including_vcredist for details - The Microsoft .NET Framework 4.5.2 Developer Pack for Visual Studio 2012 and Visual Studio 2013 is available here: https://www.microsoft.com/en-gb/download/details.aspx?id=42637

      1. AnyCPU Support
Newer versions now support targeting `AnyCPU`, see https://github.com/cefsharp/CefSharp/issues/1714 for details on how to implement this. The same technique can be used to move `libcef.dll`, etc to a different folder or common location on disk.
    1. Need to Know/Limitations
- Specifying a `CachePath` is required for persistence of cookies, saving of passwords, etc, an `In-Memory` cache is used by default (similar to `Incogneto`). See [Initialize](#initialize-and-shutdown) section below for an example of Initializing CEF with a `CachePath` below. - You can clear the disk cache using see [Network.clearBrowserCache](https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-clearBrowserCache) see [#3158](https://github.com/cefsharp/CefSharp/issues/3165) for details on executing `DevTools` commands. - Add an `app.manifest` to your app for `HiDPI` support, app compatibility (running on `Windows 10`) and tooltips in `WinForms`. The examples contain sample `app.manifest` files. **This is very important** (http://magpcss.org/ceforum/viewtopic.php?f=6&t=14721) - An error in the logs similar to `Check failed: fallback_available == base::win::GetVersion() > base::win::VERSION_WIN8 (1 vs. 0)` is a sign your application needs a `app.manifest` with the relevant `compatibility` entries. - `CEF` can only be `Initialized/Shutdown` once per process, see the section below for full details, this is a limitation of the underlying `Chromium` framework. - Only runs in the default **AppDomain**, there are some workarounds like those at https://github.com/flole/CefSharp.AppDomain and https://github.com/stever/AppHostCefSharp - Due to limited resources only a single version is supported at a time, see https://github.com/cefsharp/CefSharp#release-branches to see which version is current. If you're using an older version and encounter a problem you'll have to upgrade to the current supported version. - Only runs on `Windows` and no `App Store` version. - .Net Core is supported though additional steps are required see https://github.com/cefsharp/CefSharp.MinimalExample#net-core-support - `Sandboxing` has not been implemented as it's technically infeasible to add support directly into `CefSharp`, see #697 for details. - `WinForms` on screen keyboard potentially can benefit from `disable-usb-keyboard-detect` command line argument https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277 - `WPF` users with `High DPI` monitors are recommended to install `.Net 4.6` on their target machines, as there is a bug in the `.Net Framework` that can potentially cause a `MILERR_WIN32ERROR Exception` see #2035 for details - `Google Earth` currently requires `SharedBufferArray` which is disabled by default see https://github.com/cefsharp/CefSharp/discussions/3826#discussioncomment-1383028 on how to enable.
    1. Examples
The `CefSharp` source code contains examples of many different features. There is also the `MinimalExample` project which uses the latest `Nuget` packages to provide very simple `Browser` implementations. The `MinimalExample` is the best place to get started, download this project and get it running for a base reference to make sure everything works on your system.

https://github.com/cefsharp/CefSharp.MinimalExample

    1. Logging
By default `CEF` maintains its own log file ('Debug.log') in your application's executing folder e.g. `bin`. To disable logging change `settings.LogSeverity`, and to change the file name/path use `settings.LogFile`.

When debugging a problem, the first place to check is this log file as it contains low level `Chromium` messages. If you see errors or warnings then search on http://magpcss.org/ceforum/index.php and https://bitbucket.org/chromiumembedded/cef/issues?status=new&status=open

    1. Processes
`CEF` uses the same multiprocess model as `Chromium`. The main process which handles window creation and painting is referred to as the `browser` process. This is generally the same process as the host application and the majority of the application logic will run in the browser process. Blink rendering and JavaScript execution occur in a separate `render` process. Some application logic, such as JavaScript bindings, will also run in the render process.

- `Render Processes` (--type=renderer) - V8(Javascript) and rendering - `GPU Process` (--type=gpu-process) - handles GPU accelerated compositing - `Network Service` (--type=utility --utility-sub-type=network.mojom.NetworkService) - Entire Chromium network stack runs in this process - `Storage Service` (--type=utility --utility-sub-type=storage.mojom.StorageService) - html5 storage e.g. localStorage - `Audio Service` (--type=utility --utility-sub-type=audio.mojom.AudioService) - Playing of audio - `CrashPad Handler` (--type=crashpad-handler) - creates crash dumps

By default a spare renderer process may be created on initial browser creation or cross- origin navigation. This spare process may be used with a future cross-origin navigation or discarded on when [Cef.Shutdown](#initialize-and-shutdown) has been called.

`CefSharp` comes with a process called `CefSharp.BrowserSubProcess.exe` which will be spawned multiple times to represent separate processes as described above. Opening `Task Manager` and seeing multiple instances of `CefSharp.BrowserSubProcess.exe` is perfectly normal and required for the browser to function correctly. It is possible as of version `51.0.0` to provide your own custom `BrowserSubProcess`. An example of self hosting the BrowserSubProcess using your own executable using `WinForms` is available as part of the [MinimalExample](https://github.com/cefsharp/CefSharp.MinimalExample/blob/cefsharp/95/CefSharp.MinimalExample.WinForms/Program.PublishSingleFile.cs#L25)

Resources: - https://www.chromium.org/servicification - https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/servicification.md#overview - https://bitbucket.org/chromiumembedded/cef/issues/2498/add-support-for-site-per-process#comment-54186905 has details on the current default process model.

    1. Threads
CEF uses multiple threads for different levels of processing. The `browser` process for example contains the following commonly-referenced threads:
  • _UI thread_ is the main thread in the browser process. By default `CefSharp` uses `setting.MultiThreadedMessageLoop = true` so the `CEF UI` thread is different to your main application thread
  • _IO thread_ is used in the browser process to process IPC and network messages
  • _FILE thread_ is used in the browser process to interact with the file system
  • _RENDERER thread_ is the main thread in the renderer process
    1. Initialize and Shutdown
Can only called `Initialize` once per process (application). Running multiple instances of your app is possible, you'll need to provide unique `CachePath` for each instance, see `CefSettings` section below.

See [Request](#request-context-browser-isolation) for details on how to change settings at runtime, isolate browser instances, set different cache paths for different instances.

It's important to note that it's necessary to initialize the underlying `CEF` library. This can be achieved in one of two ways, manually calling `Cef.Initialize` before creating your first `ChromiumWebBrowser` instance, secondly if you don't need to specify any custom settings then the first `ChromiumWebBrowser` instance you create will call `Cef.Initialize` if it hasn't been called already. For those wishing to specify some custom settings then you can initialize `CEF` yourself like below:

public static void Init()
{
    var settings = new CefSettings();

    // Increase the log severity so CEF outputs detailed information, useful for debugging
    settings.LogSeverity = LogSeverity.Verbose;
    // By default CEF uses an in memory cache, to save cached data e.g. to persist cookies you need to specify a cache path
    // NOTE: The executing user must have sufficient privileges to write to this folder.
    settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache");

    Cef.Initialize(settings);
}
For **Cef.Shutdown** the WinForms and WPF instances of `ChromiumWebBrowser` the relevant Application Exit event is hooked and Cef.Shutdown() called by default. Set `CefSharpSettings.ShutdownOnExit` = false; to disable this behavior. This value needs to be set before the first instance of `ChromiumWebBrowser` is created as the event handlers are hooked in the static constructor for the `ChromiumWebBrowser` class.

It's important to note CEF that `Initialize`/`Shutdown` ***MUST*** be called on your main application thread (typically the UI thread). If you call them on different threads, your application will hang.

An example of calling `Initialize`/`Shutdown` manually using `WinForms`, the same can be applied to `WPF` and console applications that use the `CefSharp.OffScreen` package (The `OffScreen` example at https://github.com/cefsharp/CefSharp.MinimalExample is an excellent place to start, there is also one in the main project repository that's a little more advanced).

public class Program
{
        [STAThread]
        public static void Main()
        {
            //For Windows 7 and above, best to include relevant app.manifest entries as well
            Cef.EnableHighDPISupport();
	    
            //We're going to manually call Cef.Shutdown below, this maybe required in some complex scenarios
            CefSharpSettings.ShutdownOnExit = false;

            //Perform dependency check to make sure all relevant resources are in our output directory.
            Cef.Initialize(new CefSettings(), performDependencyCheck: true, browserProcessHandler: null);

            var browser = new BrowserForm();
            Application.Run(browser);
	    
            //Shutdown before your application exists or it will hang.
            Cef.Shutdown();
        }
}

In summary - `Cef.Initialize` and `Cef.Shutdown` can only called Initialize once per process(application). There is no way around this, so only call `Shutdown` when you're finished using `CefSharp`. - `Cef.Initialize` and `Cef.Shutdown` must be called on the same thread. - `Cef.Initialize` will be called for you implicitly if you create a new `ChromiumWebBrowser` instance and haven't already called `Cef.Initialize`. - For the WinForms and WPF instances of `ChromiumWebBrowser` the relevant Application Exit event is hooked and `Cef.Shutdown()` called by default. Set `CefSharpSettings.ShutdownOnExit = false;` to disable this behavior. This value needs to be set before the first instance of `ChromiumWebBrowser` is created as the event handlers are hooked in the static constructor for the `ChromiumWebBrowser` class. - In `CefSharp.OffScreen` you must explicitly call `Cef.Shutdown()` before your application exists or it will hang.

    1. CefSettings and BrowserSettings
The `CefSettings` structure allows configuration of application-wide CEF settings. Some commonly configured members include:
  • `AcceptLanguageList` comma delimited ordered list of language codes without any whitespace that will be used in the "Accept-Language" HTTP header.
  • `BrowserSubprocessPath` The path to a separate executable that will be launched for sub-processes. Typically there is no need to change this.
  • `MultiThreadedMessageLoop` the default is True in `CefSharp`, though it is possible to integrate `CEF` into your apps existing message loop see [MultiThreadedMessageLoop](#multithreadedmessageloop) below.
  • `CommandLineArgsDisabled` Set to true to disable configuration of browser process features using standard CEF and Chromium command-line arguments.
  • `RootCachePath` The root directory that all `CefSettings.CachePath` and `RequestContextSettings.CachePath` values must have in common. If this value is empty and `CefSettings.CachePath` is non-empty then it will default to the `CefSettings.CachePath` value. If this value is non-empty then it must be an absolute path. A non-empty RootCachePath can be used in conjunction with an empty CefSettings.CachePath in instances where you would like browsers attached to the Global RequestContext (the default) created in "incognito mode" and instances created with a custom RequestContext using a disk based cache. See the [RequestContext](#request-context-browser-isolation) section below for more details.
  • `CachePath` The location where data for the global browser cache will be stored on disk. In this value is non-empty then it must be an absolute path that is must be either equal to or a child directory of CefSettings.RootCachePath (if RootCachePath is empty it will default to this value). If the value is empty then browsers will be created in "incognito mode" where in-memory caches are used for storage and no data is persisted to disk. HTML5 databases such as localStorage will only persist across sessions if a cache path is specified. Can be overridden for individual `RequestContext` instances via the `RequestContextSettings.CachePath` value. See the [RequestContext](#request-context-browser-isolation) section below for more details.
  • `Locale` The locale string that will be passed to Blink. If empty the default locale of "en-US" will be used. Also configurable using the "lang" command-line switch. Change this to set the Context menu language as well.
  • `LogFile` The directory and file name to use for the debug log. If empty, the default name of "debug.log" will be used and the file will be written to the application directory. Also configurable using the "log-file" command-line switch.
  • `LogSeverity` The log severity. Only messages of this severity level or higher will be logged. Also configurable using the "log-severity" command-line switch with a value of "verbose", "info", "warning", "error", "error-report" or "disable".
  • `ResourcesDirPath` The fully qualified path for the resources directory. If this value is empty the cef.pak and/or devtools_resources.pak files must be located in the module directory. Also configurable using the "resources-dir-path" command-line switch.
  • `LocalesDirPath` The fully qualified path for the locales directory. If this value is empty the locales directory must be located in the module directory. This value is ignored on Mac OS X where pack files are always loaded from the app bundle Resources directory. Also configurable using the "locales-dir-path" command-line switch.
  • `RemoteDebuggingPort` Set to a value between 1024 and 65535 to enable remote debugging on the specified port. For example, if 8080 is specified the remote debugging URL will be http://localhost:8080. CEF can be remotely debugged from any CEF or Chrome browser window. Also configurable using the "remote-debugging-port" command-line switch.


There are many settings and command line arguments that can influence the way CEF behaves. Here are some examples:

public static void Init()
{
    // Specify Global Settings and Command Line Arguments
    var settings = new CefSettings();

    // By default CEF uses an in memory cache, to save cached data e.g. to persist cookies you need to specify a cache path
    // NOTE: The executing user must have sufficient privileges to write to this folder.
    settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache");;

    // There are many command line arguments that can either be turned on or off

    // Enable WebRTC                            
    settings.CefCommandLineArgs.Add("enable-media-stream");
    
    //Disable GPU Acceleration
    settings.CefCommandLineArgs.Add("disable-gpu");

    // Don't use a proxy server, always make direct connections. Overrides any other proxy server flags that are passed.
    // Slightly improves Cef initialize time as it won't attempt to resolve a proxy
    settings.CefCommandLineArgs.Add("no-proxy-server"); 

    Cef.Initialize(settings);
}

There are some settings which can be applied to a specific `ChromiumWebBrowser` instance. If you're using `WPF`, you can specify `BrowserSettings` in `XAML`.

var browser = new ChromiumWebBrowser(url)
{
    BrowserSettings =
    {
        DefaultEncoding = "UTF-8",
        WebGl = CefState.Disabled
    }
};
<!--xmlns:cefSharpCore="clr-namespace:CefSharp;assembly=CefSharp.Core"-->
<!--xmlns:cefSharpWpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"-->

<cefSharpWpf:ChromiumWebBrowser>
    <cefSharpWpf:ChromiumWebBrowser.BrowserSettings>
        <cefSharpCore:BrowserSettings DefaultEncoding="UTF-8"/>
    </cefSharpWpf:ChromiumWebBrowser.BrowserSettings>
</cefSharpWpf:ChromiumWebBrowser>
    1. IBrowser, IFrame and IBrowserHost
The `IBrowser` and `IFrame` objects are used for sending commands to the browser and for retrieving state information in callback methods. Each `IBrowser` object will have a single main `IFrame` object representing the top-level frame and zero or more `IFrame` objects representing sub-frames.

For example, a browser that loads two HTML `<iframe></iframe>`s will have three `IFrame` objects (the top-level frame and the two `<iframe></iframe>`s).

To load a URL in the browser main frame:

browser.MainFrame.LoadUrl(someurl);

`CefSharp` provides many extension methods to make executing common tasks easier. See `WebBrowserExtensions` for the source of these methods and to get a better understanding of how common tasks are performed.

`IBrowserHost` represents the more low level browser methods.

    1. Handlers
`CefSharp` provides some events for convenience like the following (See [IWebBrowser](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IWebBrowser.htm) API doc for all common events and detailed information on their usage):

- [ConsoleMessage](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_ConsoleMessage.htm) - [StatusMessage](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_StatusMessage.htm) - [FrameLoadStart](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_FrameLoadStart.htm) - [FrameLoadEnd](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_FrameLoadEnd.htm) - [LoadError](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_LoadError.htm) - [LoadingStateChanged](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IChromiumWebBrowserBase_LoadingStateChanged.htm)

These are simple events that expose a small percentage of the underlying handlers that `CEF` provides. These events are only called for the main browser, for popup handling you can access the notifications using `IDisplayHandler` and `ILoadHandler`.

To determine when a page has finished loading I recommend using `LoadingStateChanged` over `FrameLoadEnd`. It's important to remember that **finished loading** is different to **finished rendering**. There is currently no method of determining when a web page has finished rendering (and unlikely ever will be as with features like flash, dynamic content, animations, even simple tasks like moving your mouse or scrolling will cause new frames to be rendered).

`IDialogHandler`, `IDisplayHandler`, `IDownloadHandler`, `IContextMenuHandler`, `ILifeSpanHandler`, `ILoadHandler` and `IRequestHandler` are some of the more common handlers (see the source/API doc for the rest). These simply wrap the underlying CEF handlers in a convenient .NET fashion. For example CEF's `CefDownloadHandler` is `IDownloadHandler` in `CefSharp`. Implementing these handlers will provide you access to the underlying events and callbacks that are the foundation of CEF. A number of handlers' members can be executed in an async fashion using a callback. All the handlers follow a consistent pattern: those that return a `bool` are asking you whether you'd like to handle this yourself. If no, then return `false` for the default action. Return `true` if you will handle it yourself.

They are basic interfaces which you implement and then assign to your `ChromiumWebBrowser` instance. e.g.

browser.DownloadHandler = new DownloadHandler();
Ideally you should set handlers immediately after your `ChromiumWebBrowser` instances have been instantiated. See the Example projects in the source for more detailed examples, there are currently no default implementations available so you have to implement every method. (If you wish to contribute default implementations then submit a pull request).

Some general notes about the handlers - [IDownloadHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IDownloadHandler.htm) needs to be implemented to allow downloading of files, progress notifications, pause, cancel, etc - [IRequestHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IRequestHandler.htm) is for dealing with navigation, redirects, resource load notifications etc - [IDialogHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IDialogHandler.htm) is for file dialog notifications - [IDisplayHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IDisplayHandler.htm) is for address change, status message, console message, fullscreen mode change notifications (and more) - [ILoadHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ILoadHandler.htm) is for load status some of these are mapped to events, use this for popups - [ILifeSpanHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ILifeSpanHandler.htm) is for dealing with popups and close events - [IKeyboardHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IKeyboardHandler.htm) is for keyboard events - [IJsDialogHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IJsDialogHandler.htm) is for javascript message boxes/popups - [IDragHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IDragHandler.htm) is for drag start - [IContextMenuHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IContextMenuHandler.htm) is for customising the context menu - [IFocusHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IFocusHandler.htm) is for focus related notifications - [IResourceRequestHandlerFactory](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceRequestHandlerFactory.htm) is an interface unique to CefSharp and allows for intercepting resource requests without having to implement a IRequestHandler and IResourceRequestHandler. - [IRenderProcessMessageHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IRenderProcessMessageHandler.htm) is for custom `CefSharp` messages sent from the render process - [IFindHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IFindHandler.htm) is for find notifications

It is possible to modify the response using a `ResponseFilter`. See section below.

      1. Resource Handling
CEF supports two approaches for loading resource requests from memory/disk/database

1. The [Scheme](#scheme-handler) approach allows registration of a handler for requests targeting a particular origin (scheme + domain). 2. The [Request](#request-interception) approach allows handling of arbitrary requests at the application's discretion.

Use the HTTP(S) scheme instead of a custom scheme to avoid a range of potential issues.

If you choose to use a custom scheme (anything other than `http://`, `https://`, etc) you must register it with CEF so that it will behave as expected. If you would like your custom scheme to behave similar to HTTP (support POST requests and enforce HTTP access control (CORS) restrictions) then it should be registered as a "standard" scheme. If you are planning to perform cross-origin requests to other schemes or send POST requests via `XMLHttpRequest` to your scheme handler then you should use the HTTP scheme instead of a custom scheme to avoid potential issues. `IsSecure` and `IsCorsEnabled` params were added recently.

Handlers can be used with both built-in schemes (`http://`, `https://`, etc) and custom schemes. When using a built-in scheme choose a domain name unique to your application (like `myapp` or `internal`). Implement the `ISchemeHandlerFactory` and [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) classes to handle the request and provide response data. See [ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm) for the default implementation of [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm), which has lots of useful static helper methods.

      1. Scheme Handler
Handlers can be used with both built-in schemes (HTTP, HTTPS, etc) and custom schemes. When using a built-in scheme choose a domain name unique to your application (like `myapp` or `internal`). Implement the [ISchemeHandlerFactory](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ISchemeHandlerFactory.htm) and [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) classes to handle the request and provide response data. See [ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm) for the default implementation of [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm), which has lots of useful static helper methods.

A scheme handler is registered via the [CefSettings.RegisterScheme](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_AbstractCefSettings_RegisterScheme.htm) function. For example, you can register a handler for 'localfolder://cefsharp/' requests (there is another example below and there are working examples in the project source):

settings.RegisterScheme(new CefCustomScheme
{
	SchemeName = "localfolder",
	DomainName = "cefsharp",
	SchemeHandlerFactory = new FolderSchemeHandlerFactory(rootFolder: @"..\..\..\..\CefSharp.Example\Resources",
							hostName: "cefsharp", //Optional param no hostname/domain checking if null
							defaultPage: "home.html") //Optional param will default to index.html
});

The [FolderSchemeHandlerFactory](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_SchemeHandler_FolderSchemeHandlerFactory.htm) is a simple default implementation for reading files from disk using a scheme handler. You can use either a custom scheme (In other words, you can provide a URL in the form `customscheme://folder/yourfile`) or a standard scheme (`https://`, `https://`).

An example of implementing your own factory might look like:

public class CefSharpSchemeHandlerFactory : ISchemeHandlerFactory
{
	public const string SchemeName = "custom";

	private static readonly IDictionary<string, string> ResourceDictionary;

	static CefSharpSchemeHandlerFactory()
	{
		ResourceDictionary = new Dictionary<string, string>
		{
			{ "/home.html", Resources.home_html },
			{ "/bootstrap/bootstrap.min.css", Resources.bootstrap_min_css },
			{ "/bootstrap/bootstrap.min.js", Resources.bootstrap_min_js },
			{ "/BindingTest.html", Resources.BindingTest },
			{ "/ExceptionTest.html", Resources.ExceptionTest },
			{ "/PopupTest.html", Resources.PopupTest },
			{ "/SchemeTest.html", Resources.SchemeTest }
		};
	}

	public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
	{
		//Notes:
		// - The 'host' portion is entirely ignored by this scheme handler.
		// - If you register a ISchemeHandlerFactory for http/https schemes you should also specify a domain name
		// - Avoid doing lots of processing in this method as it will affect performance.
		// - Uses the Default ResourceHandler implementation

		var uri = new Uri(request.Url);
		var fileName = uri.AbsolutePath;

		string resource;
		if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
		{
			var fileExtension = Path.GetExtension(fileName);
			return ResourceHandler.FromString(resource, , mimeType: Cef.GetMimeType(fileExtension));
		}

		return null;
	}
}

The [ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm) is provided as a default implementation of [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) and contains many static helper methods for creating classes. See the [Resource](#resourcehandler) section below for further details.

A few examples of using the static methods are:

ResourceHandler.FromStream(stream, mimeType);
ResourceHandler.FromString(htmlString, includePreamble:true, mimeType:Cef.GetMimeType(fileExtension));
ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);

Finally, you have to register this scheme handler using some code like this:

public static void Init()
{
	// Pseudo code; you probably need more in your CefSettings also.
	var settings = new CefSettings();

	settings.RegisterScheme(new CefCustomScheme
	{
		SchemeName = "custom",
		SchemeHandlerFactory = new CefSharpSchemeHandlerFactory()
	});

	Cef.Initialize(settings);
}
It's **important** that the scheme registration takes place before `Cef.Initialize()` is called.
      1. Request Interception
[IResourceRequestHandler.GetResourceRequestHandler](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IResourceRequestHandler_GetResourceHandler.htm) supports the interception of arbitrary requests. It uses the same [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) class as the scheme handler approach. The [ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm) is provided as a default implementation of [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) and contains many static helper methods for creating classes. See the [Resource](#resourcehandler) section below for further details.
public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{
	protected override IResourceHandler GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request)
	{
		//ResourceHandler has many static methods for dealing with Streams, 
		// byte[], files on disk, strings
		// Alternatively ou can inheir from IResourceHandler and implement
		// a custom behaviour that suites your requirements.
		return ResourceHandler.FromString("Welcome to CefSharp!", mimeType: Cef.GetMimeType("html"));
	}
}

public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		//Only intercept specific Url's
		if (request.Url == "http://cefsharp.test/" || request.Url == "https://cefsharp.test/")
		{
			return new CustomResourceRequestHandler();
		}

		//Default behaviour, url will be loaded normally.
		return null;
	}
}

browser.RequestHandler = new CustomRequestHandler();

The [IWebBrowser.RegisterResourceHandler](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_WebBrowserExtensions_RegisterResourceHandler.htm) and [IWebBrowser.UnRegisterResourceHandler](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_WebBrowserExtensions_UnRegisterResourceHandler.htm) extension methods provide a simple means of providing an [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) for a given `Url`.

You could for example request a fictitious URL and provide a response just as if the site was real.

      1. ResourceHandler
Both [ISchemeHandlerFactory](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ISchemeHandlerFactory.htm) and [IResourceRequestHandler.GetResourceHandler](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IResourceRequestHandler_GetResourceHandler.htm) use the [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) interface to represent the response (stream + headers + status codes, etc). There is a default implementation of [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm) which is simply [ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm).

[ResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResourceHandler.htm) contains many static methods for convenience - [ResourceHandler.FromString](https://cefsharp.github.io/api/99.2.x/html/Overload_CefSharp_ResourceHandler_FromString.htm) - [ResourceHandler.FromByteArray](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ResourceHandler_FromByteArray.htm) - [ResourceHandler.ForErrorMessage](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ResourceHandler_ForErrorMessage.htm) - [ResourceHandler.FromStream](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ResourceHandler_FromStream.htm) - [ResourceHandler.FromFilePath](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ResourceHandler_FromFilePath.htm)

A few examples of using the static methods are:

ResourceHandler.FromStream(stream, mimeType);
ResourceHandler.FromString(htmlString, includePreamble:true, mimeType:Cef.GetMimeType(fileExtension));
ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);

The source contains detailed examples of [ISchemeHandlerFactory](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ISchemeHandlerFactory.htm).

Examples of how to implement `ResourceHandler.ProcessRequestAsync`. If you require complete control then implement [IResourceHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResourceHandler.htm), however in most cases this is not necessary.

//A simple example of a ResourceHandler that downloads a file from the internet.
//A task is called to perform processing then executes the callback when the response is ready.
//You could do Database queries + html generation or other backend type operation
public class ExampleResourceHandler : ResourceHandler
{
	public override CefReturnValue ProcessRequestAsync(IRequest request, ICallback callback)
	{
		Task.Run(() =>
		{
			using (callback)
			{
				var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://raw.githubusercontent.com/cefsharp/CefSharp/master/README.md");

				var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();

				// Get the stream associated with the response.
				var receiveStream = httpWebResponse.GetResponseStream();
				var mime = httpWebResponse.ContentType;

				var stream = new MemoryStream();
				receiveStream.CopyTo(stream);
				httpWebResponse.Close();

				//Reset the stream position to 0 so the stream can be copied into the underlying unmanaged buffer
				stream.Position = 0;

				//Populate the response values
				ResponseLength = stream.Length;
				MimeType = mime;
				StatusCode = (int)HttpStatusCode.OK;
				Stream = stream;

				callback.Continue();
			}
		});

		return CefReturnValue.ContinueAsync;
	}
}
      1. Response Filtering
[IResourceRequestHandler.GetResourceResponseFilter()](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IResourceRequestHandler_GetResourceResponseFilter.htm) supports filtering of data received in response to requests. You can retrieve the raw response data, you can append data to a response, like injecting some custom CSS at the end of a file. You can rewrite a response if required. Can be used to receive the response of any request, AJAX(`XHRHttpRequest`)/POST/GET.

The basic example of getting the response as a `UTF8` string is:

public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{
	private readonly System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();

	protected override IResponseFilter GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response)
	{
		return new CefSharp.ResponseFilter.StreamResponseFilter(memoryStream);
	}

	protected override void OnResourceLoadComplete(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
	{
		//You can now get the data from the stream
		var bytes = memoryStream.ToArray();

		if (response.Charset == "utf-8")
		{
			var str = System.Text.Encoding.UTF8.GetString(bytes);
		}
		else
		{
			//Deal with different encoding here
		}
	}
}

public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		//Only intercept specific Url's
		if (request.Url == "http://cefsharp.github.io/" || request.Url == "https://cefsharp.github.io/")
		{
			return new CustomResourceRequestHandler();
		}

		//Default behaviour, url will be loaded normally.
		return null;
	}
}

browser.RequestHandler = new CustomRequestHandler();

Currently [StreamResponseFilter](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ResponseFilter_StreamResponseFilter.htm) is the only filter provided as part of the framework.

A simple response filter that copies dataIn to dataOut. Data is streamed in chunks, typically 64kb in size.

/// <summary>
/// PassThruResponseFilter - copies all data from DataIn to DataOut.
/// Upstream documentation link
/// https://magpcss.org/ceforum/apidocs3/projects/(default)/CefResponseFilter.html#Filter(void*,size_t,size_t&,void*,size_t,size_t&)
/// </summary>
public class PassThruResponseFilter : IResponseFilter
{
	bool IResponseFilter.InitFilter()
	{
		return true;
	}

	FilterStatus IResponseFilter.Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten)
	{
		if (dataIn == null)
		{
			dataInRead = 0;
			dataOutWritten = 0;

			return FilterStatus.Done;
		}

		//Calculate how much data we can read, in some instances dataIn.Length is
		//greater than dataOut.Length
		dataInRead = Math.Min(dataIn.Length, dataOut.Length);
		dataOutWritten = dataInRead;

		var readBytes = new byte[dataInRead];
		dataIn.Read(readBytes, 0, readBytes.Length);
		dataOut.Write(readBytes, 0, readBytes.Length);

		//If we read less than the total amount avaliable then we need
		//return FilterStatus.NeedMoreData so we can then write the rest
		if (dataInRead < dataIn.Length)
		{
			return FilterStatus.NeedMoreData;
		}

		return FilterStatus.Done;
	}

	public void Dispose()
	{

	}
}

See the `CefSharp.Example`project within the source for additional example implementations of [IResourceFilter](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IResponseFilter.htm). This feature is **quite complex** to implement. Make sure you read over and debug the existing examples before asking any questions.

      1. Loading HTML/CSS/JavaScript/etc from disk/database/embedded resource/stream
There are a few extension methods provided as a convenience in the [CefSharp.WebBrowserExtensions](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_WebBrowserExtensions.htm) class.
//Load a data encoded Uri
//NOTE There are limits to the size of a Data Uri, use the overload that takes a Url if you need to load large files
LoadHtml(this IWebBrowser browser, string html, bool base64Encode = false);

//Register a ResourceHandler with the `ResourceRequestHandlerFactory` and calls browser.Load
LoadHtml(this IWebBrowser browser, string html, string url)`;

//Register a resource handler with the `ResourceRequestHandlerFactory`
RegisterResourceHandler(this IWebBrowser browser, string url, Stream stream, string mimeType = 
ResourceHandler.DefaultMimeType);

//Unregister a resource handler with the `ResourceRequestHandlerFactory`
UnRegisterResourceHandler(this IWebBrowser browser, string url);

//In `WinForms` you can pass a `HtmlString` directly into the constructor and have it load as a Data Uri
new ChromiumWebBrowser((CefSharp.Web.HtmlString)"<html><body style='background:red;'>Data Uri Test</body></html>");

For more information on `data:` encoded URI, which contains the body of the request in the URI itself see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

Generating your own `Data URI` would look something like:

const string html = "<html><head><title>Test</title></head><body><h1>Html Encoded in URL!</h1></body></html>";
var base64EncodedHtml = Convert.ToBase64String(Encoding.UTF8.GetBytes(html));
browser.Load("data:text/html;base64," + base64EncodedHtml);
      1. Request Handling
[RequestHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_Handler_RequestHandler.htm) and [ResourceRequestHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_Handler_ResourceRequestHandler.htm) can be used to intercept network requests, add headers, add post data, read response headers, perform redirects, etc.

For loading resources from memory/disk/database see [Resource](#resource-handling)

The following provides an example of how to only allow loading of resources from a specific host.

public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{
	protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
	{
		if (Uri.TryCreate(request.Url, uriKind: UriKind.Absolute, out Uri result))
		{
			if (string.Compare(result.Host, "www.google.com", StringComparison.OrdinalIgnoreCase) == 0)
			{
				return CefReturnValue.Continue;
			}
		}

		return CefReturnValue.Cancel;

		//If you wanted to perform some sort of database lookup to validate the Url then you should use a Task for processing
		//System.Threading.Tasks.Task.Run(() =>
		//{
		//    using (callback)
		//    {
		//        if (Uri.TryCreate(request.Url, uriKind: UriKind.Absolute, out Uri uri))
		//        {
		//            //Query  Database here and confirm Url matches
		//            var validUrl = string.Compare(uri.Host, "www.google.com", StringComparison.OrdinalIgnoreCase) == 0;

		//            //Continue if it's a valid Url
		//            callback.Continue(validUrl);
		//        }
		//        else
		//        {
		//            //Failed to parse the Url, we'll cancel
		//            callback.Cancel();
		//        }
		//    }
		//});

		//return CefReturnValue.ContinueAsync;
	}
}

public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		//Use our custom ResourceRequestHandler to hook OnBeforeResourceLoad
		return new CustomResourceRequestHandler();

		//For the default behaviour, return null
		//url will be loaded normally.
		//return null;
	}
}

browser.RequestHandler = new CustomRequestHandler();
    1. File URI (file:///)
I strongly advise against using `file:///` when loading from local disk. Different security restrictions apply and there are many limitations. I'd suggest using a `Scheme` handler or implementing your own `IResourceRequestHandlerFactory`. (Loading a `data:` encoded URI is also pretty handy, specially for the `OffScreen` project).

If you choose to ignore this advice you'll have to resolve any issues you have using `file:///` yourself. `ceforum` is the best resource.

    1. Proxy Resolution
There are two options for configuring a proxy server. 1. CEF using the same command-line flags as Google Chrome. - See https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-proxy-resolution - If you specify a proxy using command line args you will be unable to change it at runtime using `SetPreference`. - All `ChromiumWebBrowser` instances will share the same proxy 2. Proxy settings can be set/changed at runtime using [IRequestContext.SetPreference](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IRequestContext_SetPreference.htm) - [IRequestContext.SetPreference](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IRequestContext_SetPreference.htm) must be called on the `CEF UI` thread. Use [Cef.UIThreadTaskFactory](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_Cef_UIThreadTaskFactory.htm) to spawn a task on the `CEF UI Thread`. `IRequestContextHandler` methods are already called on the `CEF UI` thread so you can call `SetPreference` directly. Alternatively call [IRequestContext.SetPreferenceAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_RequestContextExtensions_SetPreferenceAsync.htm) which can be called on any Thread (will invoke onto the CEF UI Thread if required). - It is possible to specify the proxy settings on a per `Request Context` basis allowing you to have different `ChromiumWebBrowser` instances using different proxies. - Read the [Request](#request-context-browser-isolation) section below for more details and a basic code example.
//Create a new RequestContext that users a specific proxy server
//By default an in memory cache is used
var requestContext = RequestContext
	.Configure()
	.WithProxyServer("127.0.0.1", 8080)
	.Create();

//Create a RequestContext with a proxy and cache path
//Making sure that CachePath is equal to or a child of CefSettings.RootCachePath
//See https://github.com/cefsharp/CefSharp/issues/3111#issuecomment-629713608 for more info on cache path
var requestContext = RequestContext
	.Configure()
	.WithProxyServer("127.0.0.1", 8080)
	.WithCachePath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache"))
	.Create();

//Set the proxy at runtime, the RequestContext must be initialized
Cef.UIThreadTaskFactory.StartNew(delegate
{
	string errorMessage;

	if (!requestContext.CanSetPreference("proxy"))
	{
		//Unable to set proxy, if you set proxy via command line args it cannot be modified.
	}

	success = requestContext.SetProxy("127.0.0.1", 8080, out errorMessage);
});

Some Additional examples of setting proxy using `Preferences` in `CefSharp` are available at http://stackoverflow.com/questions/36095566/cefsharp-3-set-proxy-at-runtime

      1. Proxy Authentication
If the proxy requires authentication the [IRequestHandler.GetAuthCredentials()](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IRequestHandler_GetAuthCredentials.htm) method will be executed with an `isProxy` value of `true` to retrieve the username and password.

Example of providing UserName/Password for Proxy

browser.RequestHandler = new CustomRequestHandler();

public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
	{
		//Cast IWebBrowser to ChormiumWebBrowser if you need to access the UI control
		//You can Invoke onto the UI Thread if you need to show a dialog
		var b = (ChromiumWebBrowser)chromiumWebBrowser;

		if(isProxy)
		{
			using (callback)
			{
				callback.Continue(username: "user", password: "pass");
			}

			return true;
		}

		//Return false to cancel the request
		return false;
	}
}
    1. Request Context (Browser Isolation)
You can isolate browser instances, including providing custom cache paths, different proxy settings, different cookie managers, and many other things using a `RequestContext`. In `CEF` terms the underlying class is `CefRequestContext`.

Here are some key points:

- By default a Global Request Context will be used (settings shared by all browsers) - You can change some (not all) settings at run time using `Preferences` - Don't use the command line argument if you wish to change the value using `SetPreference` - `SetPreference` must be called on the `CEF UI Thread`. Use [Cef.UIThreadTaskFactory](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_Cef_UIThreadTaskFactory.htm) to spawn a task on the `CEF UI Thread` - `WinForms`: set the `RequestContext` immediately after you create your browser instance - `OffScreen`: Pass `RequestContext` into the constructor - `WPF`: Set in your `Control`/`Window` constructor after `InitializeComponent()` is called - Set [RequestContextSettings.CachePath](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_RequestContextSettings_CachePath.htm) to persist cookies, data, localStorage, etc - [RequestContextSettings.CachePath](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_RequestContextSettings_CachePath.htm) **must** be equal to or a child of [CefSettings.RootCachePath](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_AbstractCefSettings_RootCachePath.htm) see https://github.com/cefsharp/CefSharp/issues/3111#issuecomment-629713608

//WinForms Examples - WPF and OffScreen are similar, see notes above.

//Default implementation of RequestContext
//Default settings will be used, this means an in-memory cache (no data persisted)
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext();

// Use RequestContext.Configure to create a RequestContext using a 
// Fluent style interface, can be used to set proxy, set preferences, cachePath
// Use WithSharedSettings to share settings with another RequestContext
// This one will share the same CachePath as the Global RequestContext
var ctx = RequestContext.Configure()
                .WithSharedSettings(Cef.GetGlobalRequestContext())
                .WithPreference("webkit.webprefs.minimum_font_size", 24)
                .Create();

browser = new ChromiumWebBrowser("www.google.com");
browser.RequestContext = ctx;

//CustomRequestContextHanler needs to implement `IRequestContextHandler`
//Default settings will be used, this means an in-memory cache (no data persisted)
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext(new CustomRequestContextHandler());

//Custom settings and CustomRequestContextHandler
//Use the specified cache path (if empty, in memory cache will be used). To share the global
//browser cache and related configuration set this value to match the CefSettings.CachePath
//value.
var requestContextSettings = new RequestContextSettings { CachePath = cachePath };
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext(requestContextSettings, new CustomRequestContextHandler());

See the project source for more detailed examples.

//When you are already on the CEF UI Thread you can use the following
string errorMessage;
//You can set most preferences using a `.` notation rather than having to create a complex set of dictionaries.
//Disable resizing of text areas
//The default is true, you can change to false to disable
context.SetPreference("webkit.webprefs.text_areas_are_resizable", true, out errorMessage);
//Change the minimum font size to 24pt
context.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);

//To execute on the CEF UI Thread you can use 
Cef.UIThreadTaskFactory.StartNew(delegate
{
    string errorMessage;
    //Use this to check that settings preferences are working in your code

    //the browser variable is an instance of ChromiumWebBrowser
    var success = browser.RequestContext.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);
}); 

Setting preferences in OnRequestContextInitialized (this approach is recommended for setting a proxy as it will be called before the browser attempts to load any web page)

public class RequestContextHandler : CefSharp.Handler.RequestContextHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		// Return null for the default behaviour
		// Implement CefSharp.IResourceRequestHandler or inherit from CefSharp.Handler.ResourceRequestHandler
		// To handle resource requests at the RequestContext level

		return null;
	}

	protected override void OnRequestContextInitialized(IRequestContext requestContext)
	{
		//You can set preferences here on your newly initialized request context.
		//Note, there is called on the CEF UI Thread, so you can directly call SetPreference

		//Use this to check that settings preferences are working in your code
		//You should see the minimum font size is now 24pt
		string errorMessage;
		var success = requestContext.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);

		//Set proxy to fixed_servers
		//Make sure to include using CefSharp; to access this extension method
		var setProxySuccess = requestContext.SetProxy("scheme", "host", port: 8080, out string error);

		//Alternatively you can set the proxy with code similar to the code below
		//https://stackoverflow.com/questions/36095566/cefsharp-3-set-proxy-at-runtime has some additional examples
		//var v = new Dictionary<string, object>
		//{
		//    ["mode"] = "fixed_servers",
		//    ["server"] = "scheme://host:port"
		//};
		//string errorMessage;
		//bool success = requestContext.SetPreference("proxy", v, out errorMessage);
	}
}
    1. Printing
The CEF API only exposes limited support for printing. There is currently no support for printing in _Kiosk Mode_ (printing to the default without a dialog). The suggested workaround is to print to `PDF` then use a `3rd party` application to print the `PDF`.

If you need better printing support then you should discuss that on `ceforum`. There are already open discussions and an open issue on the CEF Issue Tracker.

- http://magpcss.org/ceforum/viewtopic.php?f=7&t=14196 - https://bitbucket.org/chromiumembedded/cef/issues/1283/adding-print-options-to-cef3 - http://magpcss.org/ceforum/viewtopic.php?f=6&t=12567&p=27604

    1. High DPI Displays/Support
Desktop applications using `WinForms/WPF` need to be made [DPI](https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows) to run correctly on a _High DPI Display_ (A display with a `DPI Scale` set greater than `100%`).
    • Note** If you mouse cursor is incorrectly positioned in the browser or the browser displays black boxes/border with rendering/resizing then your app needs to be made `DPI Aware`. Other parts of your application may also appear blurry or incorrectly-sized.
There are a number of options used to configue the DPI awareness of a process:

1. [Through](https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest) (This is generally the preferred option) 2. Via [app.config](https://docs.microsoft.com/en-us/dotnet/desktop/winforms/high-dpi-support-in-windows-forms?view=netframeworkdesktop-4.8#configuring-your-windows-forms-app-for-high-dpi-support) (**WinForms Only** where targeting .Net 4.7 and above) 3. [Programmatically](https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-programmatically)

Windows 10 1703 has additional improvements see https://blogs.windows.com/windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/ for more details.

      1. WinForms High DPI
Starting with the .NET Framework 4.7, Windows Forms includes enhancements for common high DPI and dynamic DPI scenarios. In previous versions of the .NET Framework, you used the manifest to add high DPI support. This approach is no longer recommended, since it overrides settings defined on the app.config file. Make sure to read [High](https://docs.microsoft.com/en-us/dotnet/desktop/winforms/high-dpi-support-in-windows-forms?view=netframeworkdesktop-4.8) for further details from Microsoft.
        1. App.manifest
    • IMPORTANT**
If you are targeting `.Net 4.7` or above `Microsoft` reccommends configuring `DPI Awareness` via the `app.config` rather than `app.manifest`. Make sure to read [High](https://docs.microsoft.com/en-us/dotnet/desktop/winforms/high-dpi-support-in-windows-forms?view=netframeworkdesktop-4.8) for further details from Microsoft.

Set the default awareness using the [application](https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest). The following example is PerMonitor DPI Aware on Win 10 1703 and above and PerMonitor DPI aware on older version. Make sure you read https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows#dpi-awareness-mode which discusses the different `DPI Awareness` options. If you project doesn't already have an `app.manifest` use the `Visual Studio New Item` template to add one rather than doing so manually to ensure the relevant `` entry in your `csproj/vbproj` file is added (it's a special type).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        <dpiAware>true/PM</dpiAware>
    </windowsSettings>
</application>
</assembly>
        1. Programmatically
Setting High DPI in code you can use the [Cef.EnableHighDPISupport();](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_EnableHighDPISupport.htm) helper method. This calls the `Chromium` [base::win::EnableHighDPISupport();](https://cs.chromium.org/search/?q=EnableHighDPISupport()&sq=package:chromium&type=cs) function. You then have exactly the same settings as `Chromium` uses.

[Cef.EnableHighDPISupport();](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_EnableHighDPISupport.htm) **must** be called very early on in your application execution, preferably in your application entry point (Program.Main).

The [CefSharp.MinimalExample.WinForms](https://github.com/cefsharp/CefSharp.MinimalExample/tree/master/CefSharp.MinimalExample.WinForms) project contains a working example.

      1. WPF High DPI
        1. App.manifest
Add the relevant entries see [Turn](https://github.com/microsoft/WPF-Samples/blob/master/PerMonitorDPI/readme.md#1turn-on-windows-level-per-monitor-dpi-awareness-in-appmanifest) for Microsofts reccommendations.

For a working example see https://github.com/cefsharp/CefSharp/blob/cefsharp/84/CefSharp.Wpf.Example/app.manifest for a working example. If you project doesn't already have an `app.manifest` use the `Visual Studio New Item` template to add one rather than doing so manually to ensure the relevant `` entry in your `csproj/vbproj` file is added (it's a special type).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        <dpiAware>true/PM</dpiAware>
    </windowsSettings>
</application>
</assembly>
        1. Programatically
`WPF` applications by default have an automatically generated `Program.Main` entry point which makes it harder to programatically set the `DPI`. See https://stackoverflow.com/a/26890426/4583726 for how to create a `Program.Main` then you can call [Cef.EnableHighDPISupport();](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_EnableHighDPISupport.htm). This **must** be called very early on in your application execution, preferably the first call in your custom Program.Main.
      1. OffScreen High DPI
Add the relevant `app.manifest` entries or call `Cef.EnableHighDPISupport()` (see above for an example). Read the `WinForms` section above, choose which option suites your needs.
      1. High DPI Additional Info
`Chromium` by default performs all rendering in separate sub-process. Specifically the `GPU Compositor` needs to have a `DPI Awareness` that matches your main application. Currently the default used by the `CefSharp.BrowserSubprocess.exe` is `Per Monitor DPI Aware`. As a workaround use the `disable-gpu-compositing` command line arg and the `DPI Awareness` of your main application process will be used instead of the `DPI Awareness` specified by the `GPU Process` (which is used for `GPU Compositing`). Disabling `GPU Compositing` may have an impact on performance, when https://github.com/cefsharp/CefSharp/issues/2927 is complete it will be possible to programatically set the `DPI Awareness` used by the `CefSharp.BrowserSubprocess.exe`
var settings = new CefSettings();
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Cef.Initialize(settings);

Alternatively you can try the [force-device-scale-factor](https://peter.sh/experiments/chromium-command-line-switches/#force-device-scale-factor) command line flag. This should only be used in conjunction with the WinForms implementation.

var settings = new CefSettings();
settings.CefCommandLineArgs.Add("force-device-scale-factor", "1");
Cef.Initialize(settings);

- https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx It's a very long MSDN article, but it's necessary reading if your app needs to be run on high DPI displays. - https://blogs.windows.com/windowsdeveloper/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/ - https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest

    1. MultiThreadedMessageLoop
`CefSharp` by default uses `setting.MultiThreadedMessageLoop = true`. This enables your application to get up and running very quickly, there are some important things to note and this may not be suitable for everyone.

- Uses a different thread for the message pump. - The CEF UI thread is different to your application's UI thread, which can cause some disconnects in message processing. - One example is opening a menu and clicking within the browser control with the menu staying open. - Low level Win32 messages don't propagate between `CEF` and `WinForms`

It is possible to integrate CEF into your app's existing message loop. A very minimal implementation of integrating CEF into your existing message loop involves using a timer that's called 30/60 times per second on the UI thread.

var settings = new CefSettings();
settings.MultiThreadedMessageLoop = false; //This defaults to true

Cef.Initialize(settings);

- For WPF use [DispatcherTimer](https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netframework-4.8)
- For WinForms use [Timer](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=netframework-4.8)

//Set the timer Interval to 30 times per second, can be increased to 60 if required
//For WPF
timer.Interval = TimeSpan.FromMilliseconds(1000 / 30);
//For WinForms
timer.Interval = 1000 / 30;
timer.Tick += UiThreadTimerTick;
timer.Start();

//Before closing your app
//Calling Cef.DoMessageLoopWork() after Cef.Shutdown has been called will result in
//an access violation, make sure you stop you timer first.
timer.Tick -= UiThreadTimerTick;
timer.Stop();

private void UiThreadTimerTick(object sender, EventArgs e)
{
    //Must be called on the UI Thread.
    Cef.DoMessageLoopWork();
}

A more advanced option involves setting [CefSettings.ExternalMessagePump](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_AbstractCefSettings_ExternalMessagePump.htm) and implementing [IBrowserProcessHandler.OnScheduleMessagePumpWork](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IBrowserProcessHandler_OnScheduleMessagePumpWork.htm). This allows `CEF` to notify when it needs to perform work, in some instances this may make your application more responsive. See https://github.com/cefsharp/CefSharp/issues/1748 for additional details. There are more advanced/working examples contained within the projects source.

You can hook the message loop whilst using `MultiThreadedMessageLoop`, though this is quite complex. The project source contains an example at https://github.com/cefsharp/CefSharp/blob/v53.0.0/CefSharp.WinForms.Example/BrowserTabUserControl.cs#L224 You can use this method to obtain Win32 mouse messages.

    1. Popups
A common request is to control popup creation. Implement `ILifeSpanHandler.OnBeforePopup` to control how popups are created. To cancel popup creation altogether simply `return true;`.
bool ILifeSpanHandler.OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
    //Set newBrowser to null unless you're attempting to host the popup in a new instance of ChromiumWebBrowser
    newBrowser = null;

    return true; //Return true to cancel the popup creation
}

You can cancel popup creation and open the URL in a new `ChromiumWebBrowser` instance of your choosing using this method. It is important to note the parent-child relationship will not exist using this method. So in general it's not recommended.

    • EXPERIMENTAL** OPTION 1:
Allows you to host the popup using the `newBrowser` param in `OnBeforePopup`. There are some known issues (search on the `GitHub` project). If you use this method and experience a problem then you will have to take responsibility and get the problem resolved with the CEF project. It's also important to note that the events like `LoadingStateChanged` etc are not called for popups. If you use this method, implement the relevant handler.
    • EXPERIMENTAL** OPTION 2:
Use `IWindowInfo.SetAsChild` to specify the parent handle. To use this in WPF you would need to use the WinForms host. Using this method you would need to handle the move and resize events. This would roughly look like:

- Grab the `IBrowserHost` from the newly created `IBrowser` instance that represents the popup then subscribe to window move notifications and call `NotifyMoveOrResizeStarted` - Call `SetWindowPos` on the browser HWND when the size changes (set to 0,0 when hidden to stop rendering)

There are examples in the project source though they are **EXPERIMENTAL** and there are no guarantees they're working. Option 2 has an incomplete example though reports suggest it works well, though the person never contributed a working example.

    1. JavaScript Integration
      1. 1. How do you call a JavaScript method from .NET?
Simple code may look something like this:
//There are a number of extension methods that simplify execution, they all work on the main frame
//They all exists in the CefSharp.WebBrowserExtensions class, make sure you add "using CefSharp;"
browser.ExecuteScriptAsync("document.body.style.background = 'red';");

// When executing multiple statements, group them together in an IIFE
// https://developer.mozilla.org/en-US/docs/Glossary/IIFE
// For Google.com pre-populate the search text box and click the search button
browser.ExecuteJavaScriptAsync("(function(){ document.getElementsByName('q')[0].value = 'CefSharp Was Here!'; document.getElementsByName('btnK')[0].click(); })();");

If you have a web page with multiple frames you can execute the script on the sub frames

browser.GetBrowser().GetFrame("SubFrame").ExecuteJavaScriptAsync("document.body.style.background = 'red';");
        1. When can I start executing JavaScript?
`JavaScript` can only be executed within a [V8Context](https://github.com/v8/v8/wiki/Embedder's-Guide#contexts). The `IRenderProcessMessageHandler.OnContextCreated` and `IRenderProcessMessageHandler.OnContextReleased` provide a boundary for when JavaScript can be executed. `OnContextCreated/OnContextReleased` will be called once per frame, use `frame.IsMain` to check for the main frame.

It's tempting to start trying to access the `DOM` in `OnFrameLoadStart`, whilst the `V8Context` will have been created and you will be able to execute a script the `DOM` will not have finished loading. If you need to access the `DOM` at it's earliest possible point then subscribe to `DOMContentLoaded`, some examples of executing `JavaScript` are below.

browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();

public class RenderProcessMessageHandler : IRenderProcessMessageHandler
{
  // Wait for the underlying JavaScript Context to be created. This is only called for the main frame.
  // If the page has no JavaScript, no context will be created.
  void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
  {
    const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";

    frame.ExecuteJavaScriptAsync(script);
  }
}

//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{
  //Wait for the Page to finish loading
  if (args.IsLoading == false)
  {
    browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
  }
}

//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{
  //Wait for the MainFrame to finish loading
  if(args.Frame.IsMain)
  {
    args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
  }
};

Some notes about executing `JavaScript`:

- Scripts are executed at the frame level, and every page has at least one frame (`MainFrame`). - The `IWebBrowser.ExecuteScriptAsync` extension method is left for backwards compatibility, you can use it as a shortcut to execute `js` on the main frame. - If a frame does not contain JavaScript then no `V8Context` will be created. - For a frame that doesn't have a context executing a script once the frame has loaded it's possible to create a V8Context using `IFrame.ExecuteJavaScriptAsync`. - The `DOM` won't have finished loading when `OnFrameLoadStart` is fired - `IRenderProcessMessageHandler.OnContextCreated/OnContextReleased` are only called for the main frame.

      1. 2. How do you call a JavaScript method that returns a result?
If you need to call(evaluate) JavaScript which returns a value, use one of the following:
//An extension method that evaluates JavaScript against the main frame.
JavascriptResponse response = await browser.EvaluateScriptAsync(script);
//Evaluate javascript directly against a frame
JavascriptResponse response = await frame.EvaluateScriptAsync(script);

//An extension method that evaluates Javascript Promise against the main frame.
//Uses Promise.resolve to return the script execution into a promise regardless of the return type
//This method differs from EvaluateScriptAsync in that your script **must return** a value
//Examples below
JavascriptResponse response = await browser.EvaluateScriptAsPromiseAsync(script);

JavaScript code is executed asynchronously and as such returns [Task](https://cefsharp.github.io/api/99.2.x/html/Properties_T_CefSharp_JavascriptResponse.htm) which contains error message, result and a success (`bool`) flag. Here are the fundamentals you need to know when evaluating `JavaScript`

- Please **make sure you read** [When](#when-can-i-start-executing-javascript). - Scripts are executed at the Frame level, and every page has at least one frame (`MainFrame`). - Scripts are executed in the render process and are transmitted via `IPC`, return **only the data you require** for performance reasons. - Primitive data types int, double, date, bool and string are supported. - Objects are supported to a degree and will be returned as `IDictionary<string,></string,>`, the use of the `dynamic` keyword is supported to make accessing the property values easier. - You cannot directly return a `DOM` [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) (or anything with a cyclic reference), you need to create a new object that has just the information you need to return. - Arrays containing the primitives listed above and objects are supported and will be returned as `IList<object></object>`. - `Array Like` objects like [HTMLCollection](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) cannot be returned directly use [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) to return and array - There are limits to the complexity of the object graph that can be returned (graphs with cyclic references aren't supported currently), in these cases you may need to turn the JavaScript object into a JSON string with the JavaScript `JSON.stringify()` method and return that string to your .NET code. Then you can decode that string into a .NET object with something like JSON.NET. See [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) for more details. See https://stackoverflow.com/a/46881092/4583726 for some guidence on using `JSON.stringify` with `HTMLElement`.

//Start with something simple, the following will return the value 2 as type int
//Don't use the `return` keyword
//chromiumWebBrowser.EvaluateScriptAsync executes on the main frame, scripts can be executed
//per frame if required
JavascriptResponse response = await frame.EvaluateScriptAsync("1 + 1");
JavascriptResponse response = await chromiumWebBrowser.EvaluateScriptAsync("1 + 1");

var onePlusOne = (int)response.Result;

//A javascript IFFE will be evaluated and it's result returned.
//https://developer.mozilla.org/en-US/docs/Glossary/IIFE
//If you want to execute multiple lines of javascript then an IIFE is recommended to
//avoid any variable scoping issues
var script = @"(function() { let val = 1 + 1; return val; })();";
JavascriptResponse response = await frame.EvaluateScriptAsync(script);

var onePlusOne = (int)response.Result;

//If your script uses a Promise then you must use the EvaluateScriptAsPromiseAsync method, it differs slightly
//in that you must return the value.
//The following will return a Promise that after one second resolves with a simple objec
var script = "return new Promise(function(resolve, reject) { setTimeout(resolve.bind(null, { a: 'CefSharp', b: 42, }), 1000); });"
JavascriptResponse javascriptResponse = await browser.EvaluateScriptAsPromiseAsync(script);
//You can access the object using the dynamic keyword for convenience.
dynamic result = javascriptResponse.Result;
var a = result.a;
var b = result.b;

//EvaluateScriptAsPromiseAsync calls Promise.resolve internally so even if your code doesn't
//return a Promise it will still execute successfully.
var script = @"return (function() { return 1 + 1; })();";
JavascriptResponse response = await frame.EvaluateScriptAsPromiseAsync(script);

// An example that gets the Document Height
var task = frame.EvaluateScriptAsync("(function() { var body = document.body, html = document.documentElement; return  Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); })();");

//Continue execution on the UI Thread
task.ContinueWith(t =>
{
    if (!t.IsFaulted)
    {
        var response = t.Result;
        EvaluateJavaScriptResult = response.Success ? (response.Result ?? "null") : response.Message;
    }
}, TaskScheduler.FromCurrentSynchronizationContext());

//HTMLElement/HTMLCollection Examples
//As stated above, you cannot return a HTMLElement/HTMLCollection directly.
//It's best to return only the data you require, here are some examples of using Array.from to convert a HTMLCollection  into an array of objects
//which can be returned to your .Net application.

//Get all the span elements and create an array that contains their innerText
var script = @"Array.from(document.getElementsByTagName('span')).map(x => ( x.innerText));";
JavascriptResponse response = await frame.EvaluateScriptAsync(script);

//Get all the a tags and create an array that contains a list of objects 
//Second param is the mapping function
var script = @"Array.from(document.getElementsByTagName('a'), x => ({ innerText : x.innerText, href : x.href }));";
JavascriptResponse response = await frame.EvaluateScriptAsync(script);

//List of Links, click represents a function pointer which can be used to execute the link click)
//In .Net the https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IJavascriptCallback.htm is used
//to represent the function.
var script = @"Array.from(document.getElementsByTagName('a')).map(x => ({ innerText: x.innerText, click: x.click}));";
JavascriptResponse response = await frame.EvaluateScriptAsync(script);

//Execute the following against google.com to get the `I'm Feeling Lucky` button then click the button in .Net
//NOTE: This is a simple example, you could return an aggregate object consisting of data from multiple html elements.
const string script = @"(function()
{
  let element = document.getElementsByName('btnI')[0];
  let obj = {};
  obj.id = element.id;
  obj.nodeValue = element.nodeValue;
  obj.localName = element.localName;
  obj.tagName = element.tagName;
  obj.innerText = element.innerText;
  obj.click = element.click;
  obj.attributes = Array.from(element.attributes).map(x => ({name: x.name, value: x.value}));

  return obj;
})();";

var javascriptResponse = await chromiumWebBrowser.EvaluateScriptAsync(script);
dynamic result = javascriptResponse.Result;
var clickJavascriptCallback = (IJavascriptCallback)result.click;
await clickJavascriptCallback.ExecuteAsync();
//Dispose of the click callback when done
clickJavascriptCallback.Dispose();

//Get all rows/cells for the first table in the DOM
//dynamic can be used in c# to simplify accessing the result
var response = await chromiumWebBrowser.EvaluateScriptAsync(@"(function (){
	let srcTable = document.getElementsByTagName('table')[0];
	return Array.from(srcTable.rows, row => Array.from(row.cells, cell => cell.innerText));
	})();
");

dynamic arr = response.Result;

foreach(dynamic row in arr)
{
	foreach(var cell in row)
	{
		var data = (string)cell;
	}
}

Additional examples see [Gist](https://gist.github.com/amaitland/9d354376960b0cd9305a#file-oneplusone-cs)

      1. 3. How do you expose a .NET class to JavaScript?
JavaScript Binding (`JSB`) allows for communication between `JavaScript` and `.Net`. There are two distinct implementations available currently, the `Async` version and the older `Sync` version. The `Sync` version is no longer being actively developed, it relies on `WCF` which is not available in `.Net Core` or the upcoming `.Net 5.0`.
        1. Async JavaScript Binding (JSB)
    • Summary**
- Uses `Native Chromium IPC` to pass messages back and forth between the Browser Process and Render Process., and as such is very fast. - Only `methods` are supported as the `Native Chromium IPC` is message based and can only be used in an `async` fashion (`Property` get/sets cannot be done in an async fashion) - `Methods` can return simple objects, `structs` and `classes` are supported, only a copy of the `Properties` is transferred to `JavaScript`. Think of it like making a `webservice/ajax` call, you get a response object. - Supports `JavaScript callbacks`, through the [IJavascriptCallback](https://github.com/cefsharp/CefSharp/wiki/Advanced-Async-JavaScript-Binding-(JSB)#javascript-callback) - All method calls are non blocking and return a standard [JavaScript](https://developers.google.com/web/fundamentals/primers/promises) that can be `awaited`. - Method names are transformed to `CamelCase` (the first letter is transformed to lowercase, `MyFunction` becomes `myFunction`) by default. This is configurable by setting [browser.JavascriptObjectRepository.NameConverter](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_IJavascriptObjectRepository_NameConverter.htm) property before registering your objects, set to null to disable name conversion, detailed example below. - [JavaScript](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API) details the different methods available. - `Exceptions` in `.Net` are caught and the `Promise` will be `rejected`.

- See the [Advanced](https://github.com/cefsharp/CefSharp/wiki/Advanced-Async-JavaScript-Binding-(JSB)), **please make sure you finish reading this first**.

If you are not familiar with all `Chromium` has to offer when it comes to `async programming` here are some very useful articles

- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise - https://developers.google.com/web/fundamentals/primers/async-functions - https://developers.google.com/web/fundamentals/primers/promises

          1. Binding an `Async` object in `JavaScript`
The [CefSharp.BindObjectAsync](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpbindobjectasyncsettings-objectname) method is called in `Javascript` to bind an object. [CefSharp.BindObjectAsync](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpbindobjectasyncsettings-objectname) returns a [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) that is resolved when bound object(s) are available. Objects are created in the global context (properties of the `window` object). If you call `CefSharp.BindObjectAsync` without any params then all registered objects will be bound. Binding by name is the more descriptive options.
    • The simple workflow would look like:**
- **Step 1** Create a class that you wish to expose to javascript (don't use your `Form/Window` or `Control`) - **Step 2** Register an instance of your class with the `JavaScriptObjectRepository` - **Step 3** Call [CefSharp.BindObjectAsync](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpbindobjectasyncsettings-objectname) with the name of the object you wish to register, e.g. `CefSharp.BindObjectAsync("boundAsync");` (Objects will only be available after the `Promise` has resolved.
    • Only Methods are supported.** If you need to set a property, then create `Get`/`Set` methods.
    • Step 1** Create a class
A simple class would look like this:
public class BoundObject
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}
    • Step 2** Register an instance of your class with the `JavaScriptObjectRepository`
The second part of the process is registering the object with the `JavascriptObjectRepository` (accessible though the [browser.JavascriptObjectRepository](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_IWebBrowser_JavascriptObjectRepository.htm) property). You have two options for registering an object in `.Net`, the first is registered in advance, this is usually done immediately after you create a `ChromiumWebBrowser` instance. The second options is more flexible and allows objects to be `Resolved` when required.

First Option:

//For async object registration (equivalent to the old RegisterAsyncJsObject)
browser.JavascriptObjectRepository.Register("boundAsync", new BoundObject(), true, BindingOptions.DefaultBinder);

Second Option (Preferred):

browser.JavascriptObjectRepository.ResolveObject += (sender, e) =>
{
	var repo = e.ObjectRepository;
	if (e.ObjectName == "boundAsync")
	{
		BindingOptions bindingOptions = null; //Binding options is an optional param, defaults to null
		bindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objects
		bindingOptions = new BindingOptions { Binder = new MyCustomBinder() }); //Specify a custom binder
		repo.NameConverter = null; //No CamelCase of Javascript Names
		//For backwards compatability reasons the default NameConverter doesn't change the case of the objects returned from methods calls.
		//https://github.com/cefsharp/CefSharp/issues/2442
		//Use the new name converter to bound object method names and property names of returned objects converted to camelCase
		repo.NameConverter = new CamelCaseJavascriptNameConverter();
		repo.Register("boundAsync", new BoundObject(), isAsync: true, options: bindingOptions);
	}
};

To be notified in `.Net` when objects have been bound in `JavaScript` then you can subscribe to [ObjectBoundInJavascript](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IJavascriptObjectRepository_ObjectBoundInJavascript.htm) event or the [ObjectsBoundInJavascript](https://cefsharp.github.io/api/99.2.x/html/E_CefSharp_IJavascriptObjectRepository_ObjectsBoundInJavascript.htm) event (both events are very similar obviously).

browser.JavascriptObjectRepository.ObjectBoundInJavascript += (sender, e) =>
{
	var name = e.ObjectName;

	Debug.WriteLine($"Object {e.ObjectName} was bound successfully.");
};    
<script type="text/javascript">
(async function()
{
	await CefSharp.BindObjectAsync("boundAsync");
	
	//The default is to camel case method names (the first letter of the method name is changed to lowercase)
	boundAsync.add(16, 2).then(function (actualResult)
	{
		const expectedResult = 18;
		assert.equal(expectedResult, actualResult, "Add 16 + 2 resulted in " + expectedResult);
	});
})();

When a [CefSharp.BindObjectAsync](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpbindobjectasyncsettings-objectname) call is made, the `JavascriptObjectRepository` is queried to see if an object with the given name is already registered, if no matching object is found then the `ResolveObject` event is raised. For calls to [CefSharp.BindObjectAsync](https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpbindobjectasyncsettings-objectname) without any params, then if objects have already been registered then they will all be bound, if no objects have been registered then `ResolveObject` will be called with the `ObjectName` set to `All`.

Only the basics are covered in this section, there are many advanced options check out [Advanced](https://github.com/cefsharp/CefSharp/wiki/Advanced-Async-JavaScript-Binding-(JSB))

If you'd like to see a working example then checkout [CefSharp](https://github.com/cefsharp/CefSharp.MinimalExample/tree/demo/javascriptbinding) branch, specifically [commit](https://github.com/cefsharp/CefSharp.MinimalExample/commit/9f817888fad8e5f7c602f90170102cec2db29a5f)

---

        1. Sync JavaScript Binding (JSB)
    • THIS IS A LEGACY FEATURE** - anyone creating a new application use the [Async](#async-javascript-binding-jsb) implementation as it's under active development. The `Sync` version will only receive bug fixes for regressions.
- Uses a `WCF` service for communication (Microsoft have not included support for `WCF` in `.Net Core/.Net 5.0`, there's no long term future for `WCF`). - Supports both methods and properties - Calls are executed in a `sync` fashion and are blocking, long running calls will block the `Render Process` and make your app appear slow or become unresponsive. - Supports semi complex object structures - Occasionally the `WCF` service doesn't shutdown cleanly and slows down application shutdown
          1. Binding an object in `JavaScript`
Binding is initiated by JavaScript, the `CefSharp.BindObjectAsync` method returns a `Promise` that is resolved when bound objects are available. Objects are created in the global context (properties of the `window` object). If you call `CefSharp.BindObjectAsync` without any params then all registered objects will be bound. Binding by name is the more descriptive options.

The simple workflow would look like:

- **Step 1** Create a class that you wish to expose to JavaScript (don't use your `Form/Window` or `Control`) - **Step 2** Call `CefSharp.BindObjectAsync` with the name of the object you wish to register, e.g. `CefSharp.BindObjectAsync("myObject");` (Objects will only be available after the `Promise` has resolved. - **Step 3** Register your object with the `JavaScriptObjectRepository`

    • Step 1**
public class BoundObject
{
    public string MyProperty { get; set; }
    public void MyMethod()
    {
        // Do something really cool here.
    }
    
    public void TestCallback(IJavascriptCallback javascriptCallback)
    {
        const int taskDelay = 1500;

        Task.Run(async () =>
        {
            await Task.Delay(taskDelay);

            using (javascriptCallback)
            {
                //NOTE: Classes are not supported, simple structs are
                var response = new CallbackResponseStruct("This callback from C# was delayed " + taskDelay + "ms");
                await javascriptCallback.ExecuteAsync(response);
            }
        });
    }
}
    • Step 2** Call `CefSharp.BindObjectAsync`, Some examples below of `Binding` an object look like:
    • NOTE** This is a two part process, see below the examples for details
<script type="text/javascript">
(async function()
{
	await CefSharp.BindObjectAsync("boundAsync");
	
	boundAsync.div(16, 2).then(function (actualResult)
	{
		const expectedResult = 8
		assert.equal(expectedResult, actualResult, "Divide 16 / 2 resulted in " + expectedResult);
	});
	
	boundAsync.error().catch(function (e)
    	{
        	var msg = "Error: " + e + "(" + Date() + ")";
    	});
})();

(async () =>
{
	await CefSharp.BindObjectAsync("boundAsync");
	
	boundAsync.hello('CefSharp').then(function (res)
	{
		assert.equal(res, "Hello CefSharp")
	});
})();

CefSharp.BindObjectAsync("boundAsync2").then(function(result)
{
	boundAsync2.hello('CefSharp').then(function (res)
	{
		assert.equal(res, "Hello CefSharp")
                // NOTE the ability to delete a bound object
		assert.equal(true, CefSharp.DeleteBoundObject("boundAsync2"), "Object was unbound");
		assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined");
	});
});
</script>
    • Step 3**
The second part of the process is registering the object with the `JavascriptObjectRepository` (accessible though the `browser.JavascriptObjectRepository` property). You have two options for registering an object in `.Net`, the first is registered in advance, this is usually done immediately after you create a `ChromiumWebBrowser` instance. The second options is more flexible and allows objects to be `Resolved` when required.

When a `CefSharp.BindObjectAsync` call is made, the `JavascriptObjectRepository` is queries to see if an object with the given name is specified is already registered, if no matching object is found then the `ResolveObject` event is raised. For calls to `CefSharp.BindObjectAsync` without any params, then if objects have already been registered then they will all be bound, if no objects have been registered then `ResolveObject` will be called with the `ObjectName` set to `All`.

//When a 
browser.JavascriptObjectRepository.ResolveObject += (sender, e) =>
{
	var repo = e.ObjectRepository;
	if (e.ObjectName == "boundAsync2")
	{
		BindingOptions bindingOptions = null; //Binding options is an optional param, defaults to null
		bindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objects,
		bindingOptions = new BindingOptions { Binder = new MyCustomBinder() }); //No camelcase of names and specify a custom binder
		//For backwards compatability reasons the default NameConverter doesn't change the case of the objects returned from methods calls.
		//https://github.com/cefsharp/CefSharp/issues/2442
		//Use the new name converter to bound object method names and property names of returned objects converted to camelCase
		repo.NameConverter = new CamelCaseJavascriptNameConverter();
		repo.Register("bound", new BoundObject(), isAsync: false, options: bindingOptions);
	}
};

In the actual JS code, you would use the object like this (default is to CamelCase Javascript Names, this is controllable via [JavascriptObjectRepository.NameConverter](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_IJavascriptObjectRepository_NameConverter.htm), see above for an example).

bound.myProperty; // use this syntax to access the property
bound.myMethod(); // use this to call the method.
bound.testCallback(callback); //Pass a function in to use as a callback

_**Please note:**_ - **DO NOT REGISTER YOUR FORM/WINDOW/CONTROL**. Create a class and proxy calls if required. - By default, methods and properties are changed into _camelCase_ (i.e. the first letter is lower-cased) to make its usage be natural in JavaScript code. To disable set [browser.JavascriptObjectRepository.NameConverter](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_IJavascriptObjectRepository_NameConverter.htm) to null - Complex objects are supported for properties (where applicable) so you can now do `bound.subObject.myFunction()` and `bound.subObject.myProperty = 1`. - Complex object support for functions is now possible thorugh the `IBinder` interface, you can implement your own or use the `DefaultBinder` e.g. `repo.Register("bound", new BoundObject(), BindingOptions.DefaultBinder);`

        1. RegisterAsyncJsObject
This method has been removed. See [Async](#async-javascript-binding-jsb) instead.
        1. RegisterJsObject
This has been removed. See [Sync](#sync-javascript-binding-jsb) instead.
    1. Adobe Flash Player (Pepper Flash)
    1. Offscreen Rendering (OSR)
The WPF and OffScreen versions use the _OffScreen Rendering (OSR)_ rendering mode. In OSR mode each frame is rendered to a buffer and then either drawn on the screen as in the case of WPF, or made available as a `Bitmap` in the `OffScreen`.
      1. WPF
For the WPF control, user input (mouse clicks/moves and key presses) is forwarded to the underlying browser though methods on the `IBrowserHost` interface. It is possible to obtain access to each `Bitmap` as it's rendered.

A special note should be made about hosting the `ChromiumWebBrowser` within a `ViewBox`. This is far from ideal, as every frame is rendered then post-processing happens to resize/scale the image. This is a huge performance hit and often reduces quality (it's usually quite blurry). You can adjust the resize quality using `RenderOptions.SetBitmapScalingMode`. It's best to avoid using a `ViewBox`. You can scale the content contained within the browser by adjusting the `ZoomLevel`, which is by far the most performant option.

      1. OffScreen
For the `CefSharp.OffScreen` package, each frame is rendered to a `Bitmap` and exposed for use. If you wish to interact with the browser via keyboard or mouse, you can use methods on the `IBrowser` host interface. Simulating key presses and mouse clicks/moves can be quite complex. You can use the WPF control as a starting example as it uses the same methods (add debugging to see what sequence of events is required). Key presses and mouse clicks/moves are often made up of multiple parts, `up`/`down` with a number of other possible combinations.
    1. UserAgent
1) You can specify a custom UserAgent by setting the [CefSettings.UserAgent](https://cefsharp.github.io/api/99.2.x/html/P_CefSharp_CefSettingsBase_UserAgent.htm) property. This change is applied globally and will be the default for all browser instances.
var settings = new CefSettings();
settings.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 - Testing 123";
Cef.Initialize(settings);

2) Starting with version `85`, the `UserAgent` can be changed at runtime using the DevTools protocol.

using (var client = chromiumWebBrowser.GetDevToolsClient())
{
	_ = client.Network.SetUserAgentOverrideAsync("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 - Testing 123");
}

To change the `UserAgent` at runtime before the browser has made it's first request see https://gist.github.com/amaitland/0b05701710064203171bfd05f5002514#file-setuseragentoverrideasync-cs for an example.

3) You can modify the `User-Agent` HTTP header in `IResourceRequestHandler.OnBeforeResourceLoad`, which would need to be done for every request. What it doesn't do is change the `UserAgent` the browser reports to JavaScript.

public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{
	protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
	{
		//Set the header by name, override the existing value
		request.SetHeaderByName("user-agent", "MyBrowser CefSharp Browser", true);

		return CefReturnValue.Continue;
	}
}

public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		//Where possible only intercept specific Url's
		//Load https://www.whatismybrowser.com/detect/what-is-my-user-agent in the browser and you'll
		//see our custom user agent
		if (request.Url == "https://www.whatismybrowser.com/detect/what-is-my-user-agent")
		{
			return new CustomResourceRequestHandler();
		}

		//Default behaviour, url will be loaded normally.
		return null;
	}
}

browser.RequestHandler = new CustomRequestHandler();
    1. DevTools
You can open DevTools from within CefSharp. Not all features work. Anything that's missing needs to be implemented in CEF.
browser.ShowDevTools();

You can connect Chrome to a running instance. This will likely give you more options (not all that exist within Chrome unfortunately).

var settings = new CefSettings();
settings.RemoteDebuggingPort = 8088;
Cef.Initialize(settings);

Open `http://localhost:8088` in Chrome.

    1. Screenshots
You can capture a screenshot using the DevTools protocol, both PNG (default) and JPG formats are supported. This approach can be used in the WinForms/WPF/OffScreen versions.

- [Page.CaptureScreenshotAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_DevTools_Page_PageClient_CaptureScreenshotAsync.htm) - DevTools [Page.captureScreenshot](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot) API for upstream documentation.

The example below shows capturing with default settings. Additionally you can specify viewport size, quality, and other options.

//Make sure to dispose of our observer registration when done
//If you need to make multiple calls then reuse the devtools client
//and Dispose when done.
using (var devToolsClient = chromiumWebBrowser.GetDevToolsClient())
{
	var result = await devToolsClient.Page.CaptureScreenshotAsync();

	return result.Data;
}

The example below queries the current size of the content and captures currently rendered content. Note: Pages that delay load their content will need to be forced to render before you can take a whole page screenshot.

using (var devToolsClient = chromiumWebBrowser.GetDevToolsClient())
{
	//Get the content size
	var layoutMetricsResponse = await devToolsClient.Page.GetLayoutMetricsAsync();
	var contentSize = layoutMetricsResponse.ContentSize;

	var viewPort = new Viewport()
	{
		Height= contentSize.Height,
		Width = contentSize.Width,
		X = 0,
		Y = 0,
		Scale = 1 
	};

	// https://bugs.chromium.org/p/chromium/issues/detail?id=1198576#c17
	var result = await devToolsClient.Page.CaptureScreenshotAsync(clip: viewPort, fromSurface:true, captureBeyondViewport: true);

	return result.Data;
}

Additionally the DevTools protocol can be used to stream frames using [Page.screencastFrame](https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-screencastFrame). [Page](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_DevTools_Page_PageClient.htm) API doc for methods/events currently avaliable.

      1. OffScreen/WPF
Both `Offscreen` and `WPF` use _Offscreen Rendering (OSR)_ where every frame is rendered to a bitmap. It is still a web browser under the hood and not particularly well suited for this scenario. Here are some notes:

- Lower frame rate, to make it easier to capture frames may be worth considering - You'll need to wait a period of time after the page has finished loading to allow the browser to render - There is currently no method of determining when a web page has finished rendering (and unlikely ever will be as with features like flash, dynamic content, animations, even simple tasks like moving your mouse or scrolling will cause new frames to be rendered). - A hack method to determine when rendering has approximately finished is to have a timer that's reset every time a frame is rendered, when no additional frames are rendered then the timer will file (not ideal)

      1. WinForms
See example [above](#Screenshots)
    1. Win32 Out of Memory
When using the `32bit` version make sure your application is large address aware (handle addresses larger than 2gb)

As per suggestion in http://magpcss.org/ceforum/viewtopic.php?f=6&t=15120#p34802 it appears that setting The Large Address Aware linker setting on your application executable when running as a 32bit application may now be necessary where high memory load is experienced.

- https://msdn.microsoft.com/en-us/library/wz223b1z.aspx - https://www.nuget.org/packages/LargeAddressAware/

The default x86 SubProcess shipped with CefSharp is large address aware, you should make your application large address aware as well.

After applying the Large Address Aware linker setting to your executable, if your still experiencing the exact same problem then discuss your issue at http://magpcss.org/ceforum/viewtopic.php?f=6&t=15120

    1. Load URL with PostData
There are two methods for Loading a Url with `Post Data`, the first is to modify an existing `Request`. In the example below we'll add post data to the `Request` if we visit http://httpbin.org/post
public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
	{
		//Where possible only intercept specific Url's
		//Load http://httpbin.org/post in the browser and you'll
		//see the post data
		if (request.Url == "http://httpbin.org/post")
		{
			return new CustomResourceRequestHandler();
		}

		//Default behaviour, url will be loaded normally.
		return null;
	}
}

public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{
	protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
	{
				//Modify the request to add post data
		//Make sure to read https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
		var postData = new PostData();

		postData.AddData("test=123&data=456");

		request.Method = "POST";
		request.PostData = postData;
		//Set the Content-Type header to whatever suites your requirement
		request.SetHeaderByName("Content-Type", "application/x-www-form-urlencoded", true);
		//Set additional Request headers as required.

		return CefReturnValue.Continue;
	}
}

//Load http://httpbin.org/post in the browser to see the post data
browser = new ChromiumWebBrowser("http://httpbin.org/post");
browser.RequestHandler = new CustomRequestHandler();

The second method is to use [IFrame.LoadRequest](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IFrame_LoadRequest.htm), this method can only be used if you have first successfully performed to a domain of the same origin. For example, you must navigate to say http://httpbin.org/ before you can call [IFrame.LoadRequest](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IFrame_LoadRequest.htm) for http://httpbin.org/post.

public void LoadCustomRequestExample()
{
	var frame = browser.GetMainFrame();

	//Create a new request knowing we'd like to use PostData
	var request = frame.CreateRequest(initializePostData:true);
	request.Method = "POST";
	request.Url = "http://httpbin.org/post";
        //Set AllowStoredCredentials so cookies are sent with Request
        request.Flags = UrlRequestFlags.AllowStoredCredentials;
	request.PostData.AddData("test=123&data=456");

	frame.LoadRequest(request);
}

The [browser.LoadUrlWithPostData](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_WebBrowserExtensions_LoadUrlWithPostData.htm) extension method can be used for simple cases, it calls `LoadRequest` and the same restrictions regarding having performed a successful navigation apply.

    1. Spellchecking
By default `CefSettings.Locale` will dictate which dictionary is used, the default being `en-US`. It is possible to configure many aspects of spell checking `enable/disable` on the fly, change `dictionary` on the fly, even enable multiple dictionaries. Use `RequestContext.SetPreference` (See the `RequestContext` section of this document for details on how to set a preference).

Spellcheck can only be changed dynamically using `spellcheck.dictionaries` preference (important to use the plural version) https://bitbucket.org/chromiumembedded/cef/issues/2222/spell-checking-language-cannot-be-changed#comment-38338016

Here are some useful links

http://magpcss.org/ceforum/viewtopic.php?f=6&t=14911&p=33882&hilit=spellcheck#p33882 https://cs.chromium.org/chromium/src/components/spellcheck/browser/pref_names.cc?type=cs&q=%22spellcheck.dictionary%22&l=11 https://cs.chromium.org/chromium/src/components/spellcheck/browser/pref_names.cc?type=cs&q=%22spellcheck.dictionary%22&l=15

Not all language support spell checking, see https://magpcss.org/ceforum/viewtopic.php?f=6&t=16508#p40684

    1. WebAssembly
Is enabled by default in newer builds see https://www.chromestatus.com/feature/5453022515691520

For older versions you need to manually enable `WebAssembly` see https://bitbucket.org/chromiumembedded/cef/issues/2101/add-webassembly-support

`settings.javascript_flags` translates to `settings.JavascriptFlags = "--expose-wasm";`

    1. Exception Handling
Capturing unmanaged exceptions is difficult and `CEF` could potentially be in a corrupted state requiring your application to terminate and restart. As this is a general programming topic and outside the scope of `CefSharp` specifically here are some resources to get you started researching this for yourself.

http://stackoverflow.com/questions/233255/how-does-setunhandledexceptionfilter-work-in-net-winforms-applications https://msdn.microsoft.com/en-us/library/windows/desktop/ms680634(v=vs.85).aspx https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Application.cs,8243b844777a16c3 https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Application.cs,3192

Capturing unhandled exceptions in a mixed native/CLR environment http://www.ikriv.com/blog/?p=1440

    1. Dependency Checking
`CefSharp` has a very simple class that checks to see if the all the relevant unmanaged resources are present.
//Perform dependency check to make sure all relevant resources are in our output directory.
//https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_Initialize_1.htm
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);

//Manually check
//https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_DependencyChecker.htm
DependencyChecker.AssertAllDependenciesPresent(cefSettings.Locale, cefSettings.LocalesDirPath, cefSettings.ResourcesDirPath, cefSettings.PackLoadingDisabled, cefSettings.BrowserSubprocessPath);
``` 

It's not `100%` foolproof, if your having problems and all resources exist then disable dependency checking. There are certain scenarios when it's not going to work.

https://github.com/cefsharp/CefSharp/wiki/Output-files-description-table-%28Redistribution%29

## Multimedia (Audio/Video)

`CEF` and subsequently `CefSharp` only supports freely available audio and video codecs. To see what the version of `CefSharp` you are working with supports open http://html5test.com/ in a `ChromiumWebBrowser` instance.

- `MP3` patent has expired and as a result is supported in version `65.0.0` on wards.
- `H264/AAC` are classed as `Proprietary Codecs` and are not supported, they require you to obtain a license. Sites like `Netflix/Twitter/Instagram` use `H264` and as a result their videos won't play. See https://www.fsf.org/licensing/h264-patent-license for some comments from the `Free Software Foundation` on the subject.

Compiling `CEF` with support for `H264/AAC` is outside the scope of this project. The following are provided for reference only, please don't ask for support for compiling `CEF`.

- http://magpcss.org/ceforum/viewtopic.php?f=6&t=13515
- https://github.com/cefsharp/CefSharp/issues/1934#issuecomment-279305821
- https://github.com/mitchcapper/CefSharpDockerfiles

## OnScreen (Virtual) Keyboard

The `WinForms` version has built in support for onscreen keyboard, it has been reported that on occasion it doesn't always popup correctly, using `disable-usb-keyboard-detect` command line argument
https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277 has reported to resolve this problem.

The `WPF` does not have built in support for onscreen (virtual) keyboard, starting with version `73` a new `VirtualKeyboardRequested` event now provides notification when you application should display a virtual keyboard. Unfortunately it's difficult to provide a default implementation that supports `Windows 7, 8.1 and 10` as there is no `.Net API` for display a virtual keyboard. A `Windows 10 Only` example was added in https://github.com/cefsharp/CefSharp/commit/0b57e526158e57e522d46671404c557256529416 If you need to support `Windows 8 and 10` then https://github.com/maximcus/WPFTabTip might be useful. For `Windows 7` https://stackoverflow.com/questions/1168203/incorporating-the-windows-7-onscreen-keyboard-into-a-wpf-app has some suggestions.

## Protocol Execution

The following example calls [Process.Start](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.start?view=net-5.0) to open a `mailto` link with the default associated mail client.


```c#
public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{
	protected override bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect)
	{
		//For security reasons you should perform validation on the url to confirm that it's safe before proceeding.
		if (request.Url.StartsWith("mailto:"))
		{
			//Cancel the request and defer to Windows to open the Url with the associated
			//Handler (in this case the default email client should be opened).
			Process.Start(request.Url);
			return true;
		}
		return base.OnBeforeBrowse(chromiumWebBrowser, browser, frame, request, userGesture, isRedirect);
	}
}

browser.RequestHandler = new CustomRequestHandler();
    1. Cookie Manager
It's possible to create/retrieve/update/delete cookies via the [ICookieManager](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ICookieManager.htm). By default all browsers share the same [ICookieManager](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ICookieManager.htm) which can be accessed via [Cef.GetGlobalCookieManager](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_GetGlobalCookieManager.htm). The cookie managers storage is created in an async fashion, whilst this method may return a cookie manager instance, there may be a short delay before you can create/retrieve/update/delete cookies.
      1. Create/Update Cookie
Using [ICookieManager.SetCookie](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ICookieManager_SetCookie.htm). - Using the name of an existing cookie will update it's value/expiry/etc. - Cookie will be created in an async fashion, see [ICookieManager.SetCookieAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_AsyncExtensions_SetCookieAsync.htm) below if you need to wait for the cookie to be created.
using CefSharp;

var cookieManager = Cef.GetGlobalCookieManager();

//success == false if an invalid URL is specified or if cookies cannot be accessed.
//The cookie itself will be created/updated async, use SetCookieAsync (example below) to be sure
//the cookie has been created/updated successfully.
var success = cookieManager.SetCookie("custom://cefsharp/home.html", new Cookie
{
	Name = "CefSharpTestCookie",
	Value = "ILikeCookies",
	Expires = DateTime.Now.AddDays(1)
});

Cookies are created in an async fashion, using [ICookieManager.SetCookieAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_AsyncExtensions_SetCookieAsync.htm) if you need to make sure the cookie has been created before continuing then use

using CefSharp;

var cookieManager = Cef.GetGlobalCookieManager();

//If success == true the cookie will have been created/updated successfully.
var success = await cookieManager.SetCookieAsync("custom://cefsharp/home.html", new Cookie
{
	Name = "CefSharpTestCookie",
	Value = "ILikeCookies",
	Expires = DateTime.Now.AddDays(1)
});
      1. Delete Cookie(s)
Using [ICookieManager.DeleteCookiesAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_AsyncExtensions_DeleteCookiesAsync.htm) or [ICookieManager.DeleteCookies](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_ICookieManager_DeleteCookies.htm) you can delete just a single cookie by name, all cookies for a Url or all cookies for the [ICookieManager](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ICookieManager.htm)
using CefSharp;

var cookieManager = Cef.GetGlobalCookieManager();

//Delete cookies by name
await cookieManager.DeleteCookiesAsync(url: "custom://cefsharp/home.html", name: "CefSharpTestCookie");

//Delete all cookies for a Url
await cookieManager.DeleteCookiesAsync(url: "custom://cefsharp/home.html");

//Delete all cookies for the cookie manager
await cookieManager.DeleteCookiesAsync();
      1. Retrieve Cookies
Get all cookies for a Url using [ICookieManager.VisitUrlCookiesAsync](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_AsyncExtensions_VisitUrlCookiesAsync.htm)
using CefSharp;

var cookieManager = Cef.GetGlobalCookieManager();

//Using await
var result = await cookieManager.VisitUrlCookiesAsync("custom://cefsharp/home.html", includeHttpOnly: false);
var cookieCount = result.Count;

//Using ContinueWith
cookieManager.VisitUrlCookiesAsync("custom://cefsharp/home.html", includeHttpOnly: false).ContinueWith(t =>
{
	if (t.Status == TaskStatus.RanToCompletion)
	{
		//All the cookies for the specified URL
		var cookies = t.Result;

		foreach (var cookie in cookies)
		{
			Debug.WriteLine("CookieName: " + cookie.Name);
		}
	}
	else
	{
		Debug.WriteLine("No Cookies found");
	}
});

If you are using multiple [RequestContexts](#request-context-browser-isolation) then you can use the [IWebBrowser.GetCookieManager](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_WebBrowserExtensions_GetCookieManager.htm) extension method. (All instances of `ChromiumWebBrowser` implement `IWebBrowser`). This will also work when using the default Global Cookie Manager.

using CefSharp;

var cookieManager = chromiumWebBrowser.GetCookieManager();
var result = await cookieManager.VisitUrlCookiesAsync("custom://cefsharp/home.html", includeHttpOnly: false);
var cookieCount = result.Count;
      1. Cookie Manager Initialized
If you need to wait for the Cookie Store to initialize so you can set cookies before your first `ChromiumWebBrowser` is created there are a few options including:
using CefSharp;

public class BrowserProcessHandler : IBrowserProcessHandler
{
	void IBrowserProcessHandler.OnContextInitialized()
	{
		//The Global CookieManager has been initialized, you can now create/retrieve/update/delete cookies
		var cookieManager = Cef.GetGlobalCookieManager();

		
	}

	void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay)
	{
		
	}

	public virtual void Dispose()
	{

	}
}

var settings = new CefSettings()
{
	//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
	CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
};

Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: new BrowserProcessHandler());

Alternatively [Cef.GetGlobalCookieManager](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Cef_GetGlobalCookieManager.htm) takes a [ICompletionCallback](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_ICompletionCallback.htm) param which can be used to wait for the cookie store to be initialized. [TaskCompletionCallback](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_TaskCompletionCallback.htm) is a default implement that exposes a Task.

using CefSharp;

var callback = new TaskCompletionCallback();
var cookieManager = Cef.GetGlobalCookieManager(callback);

await callback.Task.ConfigureAwait(false);

return cookieManager;
    1. SSL/TLS Certificate Errors
        1. Option 1 (Preferred)
Implement [IRequestHandler.OnCertificateError](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IRequestHandler_OnCertificateError.htm) - this method will be called for every invalid certificate. If you only wish to override a few methods of [IRequestHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_IRequestHandler.htm) then you can inherit from [RequestHandler](https://cefsharp.github.io/api/99.2.x/html/T_CefSharp_Handler_RequestHandler.htm) and override the methods you are interested in specifically, in this case [OnCertificateError](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_Handler_RequestHandler_OnCertificateError.htm)
//Make sure you assign your RequestHandler instance to the `ChromiumWebBrowser`
browser.RequestHandler = new ExampleRequestHandler();

public class ExampleRequestHandler : RequestHandler
{
    protected override bool OnCertificateError(IWebBrowser chromiumWebBrowser, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
    {
        //NOTE: I suggest wrapping callback in a using statement or explicitly execute callback.Dispose as callback wraps an unmanaged resource.

        //Example #1
        //Return true and call IRequestCallback.Continue() at a later time to continue or cancel the request.
        //In this instance we'll use a Task, typically you'd invoke a call to the UI Thread and display a Dialog to the user
        Task.Run(() =>
        {
            //NOTE: When executing the callback in an async fashion need to check to see if it's disposed
            if (!callback.IsDisposed)
            {
                using (callback)
                {
                    //We'll allow the expired certificate from badssl.com
                    if (requestUrl.ToLower().Contains("https://expired.badssl.com/"))
                    {
                        callback.Continue(true);
                    }
                    else
                    {
                        callback.Continue(false);
                    }
                }
            }
        });

        return true;

        //Example #2
        //Execute the callback and return true to immediately allow the invalid certificate
        //callback.Continue(true); //Callback will Dispose it's self once exeucted
        //return true;

        //Example #3
        //Return false for the default behaviour (cancel request immediately)
        //callback.Dispose(); //Dispose of callback
        //return false;
    }
}
        1. Option 2
Set ignore-certificate-errors command line arg. This will ignore all certificate errors, no calls to [IRequestHandler.OnCertificateError](https://cefsharp.github.io/api/99.2.x/html/M_CefSharp_IRequestHandler_OnCertificateError.htm) will be made.
var settings = new CefSettings();
settings.CefCommandLineArgs.Add("ignore-certificate-errors");

Cef.Initialize(settings);
Clone this wiki locally