// Copyright © 2015 The CefSharp Authors. All rights reserved.
|
//
|
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
|
using System;
|
using System.Collections.Generic;
|
using System.Collections.Specialized;
|
using System.ComponentModel;
|
using System.Globalization;
|
using System.IO;
|
using System.Text;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using CefSharp.Internals;
|
using CefSharp.Web;
|
|
namespace CefSharp
|
{
|
/// <summary>
|
/// WebBrowser extensions - These methods make performing common tasks easier.
|
/// </summary>
|
public static class WebBrowserExtensions
|
{
|
public const string BrowserNullExceptionString = "IBrowser instance is null. Browser has likely not finished initializing or is in the process of disposing.";
|
public const string BrowserHostNullExceptionString = "IBrowserHost instance is null. Browser has likely not finished initializing or is in the process of disposing.";
|
public const string FrameNullExceptionString = "IFrame instance is null. Browser has likely not finished initializing or is in the process of disposing.";
|
|
#region Legacy Javascript Binding
|
/// <summary>
|
/// Registers a Javascript object in this specific browser instance.
|
/// </summary>
|
/// <param name="webBrowser">The browser to perform the registering on.</param>
|
/// <param name="name">The name of the object. (e.g. "foo", if you want the object to be accessible as window.foo).</param>
|
/// <param name="objectToBind">The object to be made accessible to Javascript.</param>
|
/// <param name="options">(Optional) binding options - camelCaseJavascriptNames default to true.</param>
|
/// <exception cref="Exception">Browser is already initialized. RegisterJsObject must be +
|
/// called before the underlying CEF browser is created.</exception>
|
[Obsolete("This method has been removed, see https://github.com/cefsharp/CefSharp/issues/2990 for details on migrating your code.")]
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
public static void RegisterJsObject(this IWebBrowser webBrowser, string name, object objectToBind, BindingOptions options = null)
|
{
|
throw new NotImplementedException("This method has been removed, see https://github.com/cefsharp/CefSharp/issues/2990 for details on migrating your code.");
|
}
|
|
/// <summary>
|
/// <para>Asynchronously registers a Javascript object in this specific browser instance.</para>
|
/// <para>Only methods of the object will be availabe.</para>
|
/// </summary>
|
/// <param name="webBrowser">The browser to perform the registering on</param>
|
/// <param name="name">The name of the object. (e.g. "foo", if you want the object to be accessible as window.foo).</param>
|
/// <param name="objectToBind">The object to be made accessible to Javascript.</param>
|
/// <param name="options">binding options - camelCaseJavascriptNames default to true </param>
|
/// <exception cref="Exception">Browser is already initialized. RegisterJsObject must be +
|
/// called before the underlying CEF browser is created.</exception>
|
/// <remarks>The registered methods can only be called in an async way, they will all return immediately and the resulting
|
/// object will be a standard javascript Promise object which is usable to wait for completion or failure.</remarks>
|
[Obsolete("This method has been removed, see https://github.com/cefsharp/CefSharp/issues/2990 for details on migrating your code.")]
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
public static void RegisterAsyncJsObject(this IWebBrowser webBrowser, string name, object objectToBind, BindingOptions options = null)
|
{
|
throw new NotImplementedException("This method has been removed, see https://github.com/cefsharp/CefSharp/issues/2990 for details on migrating your code.");
|
}
|
#endregion
|
|
/// <summary>
|
/// Returns the main (top-level) frame for the browser window.
|
/// </summary>
|
/// <param name="browser">the ChromiumWebBrowser instance.</param>
|
/// <returns> the main frame. </returns>
|
public static IFrame GetMainFrame(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var cefBrowser = browser.BrowserCore;
|
|
ThrowExceptionIfBrowserNull(cefBrowser);
|
|
return cefBrowser.MainFrame;
|
}
|
|
/// <summary>
|
/// Returns the focused frame for the browser window.
|
/// </summary>
|
/// <param name="browser">the ChromiumWebBrowser instance.</param>
|
/// <returns>the focused frame.</returns>
|
public static IFrame GetFocusedFrame(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var cefBrowser = browser.BrowserCore;
|
|
ThrowExceptionIfBrowserNull(cefBrowser);
|
|
return cefBrowser.FocusedFrame;
|
}
|
|
/// <summary>
|
/// Execute Undo on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Undo(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Undo();
|
}
|
|
/// <summary>
|
/// Execute Undo on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Undo(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Undo();
|
}
|
}
|
|
/// <summary>
|
/// Execute Redo on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Redo(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Redo();
|
}
|
|
/// <summary>
|
/// Execute Redo on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Redo(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Redo();
|
}
|
}
|
|
/// <summary>
|
/// Execute Cut on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Cut(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Cut();
|
}
|
|
/// <summary>
|
/// Execute Cut on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Cut(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Cut();
|
}
|
}
|
|
/// <summary>
|
/// Execute Copy on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Copy(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Copy();
|
}
|
|
/// <summary>
|
/// Execute Copy on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Copy(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Copy();
|
}
|
}
|
|
/// <summary>
|
/// Execute Paste on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Paste(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Paste();
|
}
|
|
/// <summary>
|
/// Execute Paste on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Paste(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Paste();
|
}
|
}
|
|
/// <summary>
|
/// Execute Delete on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Delete(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Delete();
|
}
|
|
/// <summary>
|
/// Execute Delete on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Delete(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.Delete();
|
}
|
}
|
|
/// <summary>
|
/// Execute SelectAll on the focused frame.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void SelectAll(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.SelectAll();
|
}
|
|
/// <summary>
|
/// Execute SelectAll on the focused frame.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void SelectAll(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.SelectAll();
|
}
|
}
|
|
/// <summary>
|
/// Opens up a new program window (using the default text editor) where the source code of the currently displayed web page is
|
/// shown.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void ViewSource(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.ViewSource();
|
}
|
|
/// <summary>
|
/// Opens up a new program window (using the default text editor) where the source code of the currently displayed web page is
|
/// shown.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void ViewSource(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.MainFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.ViewSource();
|
}
|
}
|
|
/// <summary>
|
/// Retrieve the main frame's HTML source using a <see cref="Task{String}"/>.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// <see cref="Task{String}"/> that when executed returns the main frame source as a string.
|
/// </returns>
|
public static Task<string> GetSourceAsync(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
return browser.BrowserCore.GetSourceAsync();
|
}
|
|
/// <summary>
|
/// Retrieve the main frame's HTML source using a <see cref="Task{String}"/>.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <returns>
|
/// <see cref="Task{String}"/> that when executed returns the main frame source as a string.
|
/// </returns>
|
public static Task<string> GetSourceAsync(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
return frame.GetSourceAsync();
|
}
|
}
|
|
/// <summary>
|
/// Retrieve the main frame's display text using a <see cref="Task{String}"/>.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// <see cref="Task{String}"/> that when executed returns the main frame display text as a string.
|
/// </returns>
|
public static Task<string> GetTextAsync(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
return browser.BrowserCore.GetTextAsync();
|
}
|
|
/// <summary>
|
/// Retrieve the main frame's display text using a <see cref="Task{String}"/>.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <returns>
|
/// <see cref="Task{String}"/> that when executed returns the main frame display text as a string.
|
/// </returns>
|
public static Task<string> GetTextAsync(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.FocusedFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
return frame.GetTextAsync();
|
}
|
}
|
|
/// <summary>
|
/// Download the file at url using <see cref="IDownloadHandler"/>.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="url">url to download</param>
|
public static void StartDownload(this IChromiumWebBrowserBase browser, string url)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.StartDownload(url);
|
}
|
|
/// <summary>
|
/// Download the file at url using <see cref="IDownloadHandler"/>.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="url">url to download</param>
|
public static void StartDownload(this IBrowser browser, string url)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.StartDownload(url);
|
}
|
|
/// <summary>
|
/// See <see cref="IChromiumWebBrowserBase.LoadUrlAsync(string)"/> for details
|
/// </summary>
|
/// <param name="chromiumWebBrowser">ChromiumWebBrowser instance (cannot be null)</param>
|
/// <summary>
|
/// Load the <paramref name="url"/> in the main frame of the browser
|
/// </summary>
|
/// <param name="url">url to load</param>
|
/// <returns>See <see cref="IChromiumWebBrowserBase.LoadUrlAsync(string)"/> for details</returns>
|
public static Task<LoadUrlAsyncResponse> LoadUrlAsync(IChromiumWebBrowserBase chromiumWebBrowser, string url)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser);
|
|
if (string.IsNullOrEmpty(url))
|
{
|
throw new ArgumentNullException(nameof(url));
|
}
|
|
var tcs = new TaskCompletionSource<LoadUrlAsyncResponse>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
EventHandler<LoadErrorEventArgs> loadErrorHandler = null;
|
EventHandler<LoadingStateChangedEventArgs> loadingStateChangeHandler = null;
|
|
loadErrorHandler = (sender, args) =>
|
{
|
//Actions that trigger a download will raise an aborted error.
|
//Generally speaking Aborted is safe to ignore
|
if (args.ErrorCode == CefErrorCode.Aborted)
|
{
|
return;
|
}
|
|
//If LoadError was called then we'll remove both our handlers
|
//as we won't need to capture LoadingStateChanged, we know there
|
//was an error
|
chromiumWebBrowser.LoadError -= loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged -= loadingStateChangeHandler;
|
|
//Ensure our continuation is executed on the ThreadPool
|
//For the .Net Core implementation we could use
|
//TaskCreationOptions.RunContinuationsAsynchronously
|
tcs.TrySetResult(new LoadUrlAsyncResponse(args.ErrorCode, -1));
|
};
|
|
loadingStateChangeHandler = (sender, args) =>
|
{
|
//Wait for IsLoading = false
|
if (!args.IsLoading)
|
{
|
//If LoadingStateChanged was called then we'll remove both our handlers
|
//as LoadError won't be called, our site has loaded with a valid HttpStatusCode
|
//HttpStatusCodes can still be for example 404, this is considered a successful request,
|
//the server responded, it just didn't have the page you were after.
|
chromiumWebBrowser.LoadError -= loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged -= loadingStateChangeHandler;
|
|
var host = args.Browser.GetHost();
|
|
var navEntry = host?.GetVisibleNavigationEntry();
|
|
int statusCode = navEntry?.HttpStatusCode ?? -1;
|
|
//By default 0 is some sort of error, we map that to -1
|
//so that it's clearer that something failed.
|
if (statusCode == 0)
|
{
|
statusCode = -1;
|
}
|
|
//Ensure our continuation is executed on the ThreadPool
|
//For the .Net Core implementation we could use
|
//TaskCreationOptions.RunContinuationsAsynchronously
|
tcs.TrySetResult(new LoadUrlAsyncResponse(statusCode == -1 ? CefErrorCode.Failed : CefErrorCode.None, statusCode));
|
}
|
};
|
|
chromiumWebBrowser.LoadError += loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged += loadingStateChangeHandler;
|
|
chromiumWebBrowser.LoadUrl(url);
|
|
return tcs.Task;
|
}
|
|
/// <summary>
|
/// This resolves when the browser navigates to a new URL or reloads.
|
/// It is useful for when you run code which will indirectly cause the browser to navigate.
|
/// A common use case would be when executing javascript that results in a navigation. e.g. clicks a link
|
/// This must be called before executing the action that navigates the browser. It may not resolve correctly
|
/// if called after.
|
/// </summary>
|
/// <remarks>
|
/// Usage of the <c>History API</c> <see href="https://developer.mozilla.org/en-US/docs/Web/API/History_API"/> to change the URL is considered a navigation
|
/// </remarks>
|
/// <param name="chromiumWebBrowser">ChromiumWebBrowser instance (cannot be null)</param>
|
/// <param name="timeout">optional timeout, if not specified defaults to five(5) seconds.</param>
|
/// <param name="cancellationToken">optional CancellationToken</param>
|
/// <returns>Task which resolves when <see cref="IChromiumWebBrowserBase.LoadingStateChanged"/> has been called with <see cref="LoadingStateChangedEventArgs.IsLoading"/> false.
|
/// or when <see cref="IChromiumWebBrowserBase.LoadError"/> is called to signify a load failure.
|
/// </returns>
|
/// <example>
|
/// <code>
|
/// <![CDATA[
|
/// string script = "document.getElementsByTagName('a')[0].click();";
|
/// await Task.WhenAll(
|
/// chromiumWebBrowser.WaitForNavigationAsync(),
|
/// chromiumWebBrowser.EvaluateScriptAsync(script));
|
/// ]]>
|
/// </code>
|
/// </example>
|
public static async Task<WaitForNavigationAsyncResponse> WaitForNavigationAsync(IChromiumWebBrowserBase chromiumWebBrowser, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser);
|
|
var tcs = new TaskCompletionSource<WaitForNavigationAsyncResponse>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
EventHandler<LoadErrorEventArgs> loadErrorHandler = null;
|
EventHandler<LoadingStateChangedEventArgs> loadingStateChangeHandler = null;
|
|
loadErrorHandler = (sender, args) =>
|
{
|
//Actions that trigger a download will raise an aborted error.
|
//Generally speaking Aborted is safe to ignore
|
if (args.ErrorCode == CefErrorCode.Aborted)
|
{
|
return;
|
}
|
|
//If LoadError was called then we'll remove both our handlers
|
//as we won't need to capture LoadingStateChanged, we know there
|
//was an error
|
chromiumWebBrowser.LoadError -= loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged -= loadingStateChangeHandler;
|
|
//Ensure our continuation is executed on the ThreadPool
|
//For the .Net Core implementation we could use
|
//TaskCreationOptions.RunContinuationsAsynchronously
|
tcs.TrySetResult(new WaitForNavigationAsyncResponse(args.ErrorCode, -1));
|
};
|
|
loadingStateChangeHandler = (sender, args) =>
|
{
|
//Wait for while page to finish loading not just the first frame
|
if (!args.IsLoading)
|
{
|
//If LoadingStateChanged was called then we'll remove both our handlers
|
//as LoadError won't be called, our site has loaded with a valid HttpStatusCode
|
//HttpStatusCodes can still be for example 404, this is considered a successful request,
|
//the server responded, it just didn't have the page you were after.
|
chromiumWebBrowser.LoadError -= loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged -= loadingStateChangeHandler;
|
|
var host = args.Browser.GetHost();
|
|
var navEntry = host?.GetVisibleNavigationEntry();
|
|
int statusCode = navEntry?.HttpStatusCode ?? -1;
|
|
//By default 0 is some sort of error, we map that to -1
|
//so that it's clearer that something failed.
|
if (statusCode == 0)
|
{
|
statusCode = -1;
|
}
|
|
//Ensure our continuation is executed on the ThreadPool
|
//For the .Net Core implementation we could use
|
//TaskCreationOptions.RunContinuationsAsynchronously
|
tcs.TrySetResult(new WaitForNavigationAsyncResponse(statusCode == -1 ? CefErrorCode.Failed : CefErrorCode.None, statusCode));
|
}
|
};
|
|
chromiumWebBrowser.LoadError += loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged += loadingStateChangeHandler;
|
|
try
|
{
|
return await TaskTimeoutExtensions.WaitAsync(tcs.Task, timeout ?? TimeSpan.FromSeconds(5), cancellationToken).ConfigureAwait(false);
|
}
|
catch (Exception)
|
{
|
chromiumWebBrowser.LoadError -= loadErrorHandler;
|
chromiumWebBrowser.LoadingStateChanged -= loadingStateChangeHandler;
|
|
throw;
|
}
|
}
|
|
/// <summary>
|
/// Waits for a DOM element specified by the <paramref name="selector"/> string to be added to or removed from the DOM.
|
/// A simplified version of Puppeteer WaitForSelector. Uses a MutationObserver to wait for the element to become added or removed.
|
/// </summary>
|
/// <param name="chromiumWebBrowser">ChromiumWebBrowser instance (cannot be null)</param>
|
/// <param name="selector">querySelector for the element e.g. #idOfMyElement</param>
|
/// <param name="timeout">timeout</param>
|
/// <param name="removed">
|
/// (Optional) if true waits for element to be removed from the DOM. If the querySelector immediately resolves
|
/// to null then the element is considered removed. If false (default) waits for the element to be added to the DOM.
|
/// </param>
|
/// <returns>A Task that resolves when element specified by selector string is added to or removed from the DOM.</returns>
|
/// <example>
|
/// <code>
|
/// <![CDATA[
|
/// string script = "const newDiv = document.createElement('div'); newDiv.id = 'myElement'; document.body.append(newDiv);";
|
/// await Task.WhenAll(
|
/// browser.WaitForSelectorAsync("#myElement");,
|
/// chromiumWebBrowser.EvaluateScriptAsync(script));
|
/// ]]>
|
/// </code>
|
/// </example>
|
/// <remarks>
|
/// This function is typically used in conjunction with javascript that directly or indirectly adds/removes an element from the DOM.
|
/// Unlike the puppeteer version navigations aren't handled internally, the method will throw a <see cref="TimeoutException"/> if a navigation
|
/// occurs whilst waiting to resolve.
|
/// </remarks>
|
public static async Task<WaitForSelectorAsyncResponse> WaitForSelectorAsync(this IWebBrowser chromiumWebBrowser, string selector, TimeSpan? timeout = null, bool removed = false)
|
{
|
const string waitForSelectorFunction = @"
|
async function waitForSelectorFunction(timeout, selector, waitForRemoved)
|
{
|
let timedOut = false;
|
if (timeout)
|
setTimeout(() => (timedOut = true), timeout);
|
|
return await pollMutation();
|
|
async function pollMutation() {
|
const success = await mutationSelector(selector, waitForRemoved);
|
if (success)
|
return Promise.resolve(success);
|
let fulfill;
|
const result = new Promise((x) => (fulfill = x));
|
const observer = new MutationObserver(async () => {
|
if (timedOut) {
|
observer.disconnect();
|
fulfill();
|
}
|
const success = await mutationSelector(selector, waitForRemoved);
|
if (success) {
|
observer.disconnect();
|
fulfill(success);
|
}
|
});
|
observer.observe(document, {
|
childList: true,
|
subtree: true,
|
attributes: false,
|
});
|
return result;
|
}
|
|
async function mutationSelector(selector, waitForRemoved)
|
{
|
const element = document.querySelector(selector);
|
|
if (!element)
|
return waitForRemoved;
|
|
if(waitForRemoved && element)
|
return null;
|
|
let obj = {};
|
obj.id = element.id;
|
obj.nodeValue = element.nodeValue;
|
obj.localName = element.localName;
|
obj.tagName = element.tagName;
|
|
return obj;
|
}
|
};";
|
|
if(chromiumWebBrowser == null)
|
{
|
throw new ArgumentNullException(nameof(chromiumWebBrowser));
|
}
|
|
if(string.IsNullOrEmpty(selector))
|
{
|
throw new ArgumentException($"{nameof(selector)} cannot be null or empty.");
|
}
|
|
var execute = GetScriptForJavascriptMethodWithArgs("waitForSelectorFunction", new object[] { timeout.HasValue ? timeout.Value.Milliseconds : 5000, selector, removed });
|
var query = @"return (async () => {" + Environment.NewLine + waitForSelectorFunction + Environment.NewLine + "return " + execute + Environment.NewLine + "})(); ";
|
|
var response = chromiumWebBrowser.EvaluateScriptAsPromiseAsync(query);
|
|
var timeoutResponse = await TaskTimeoutExtensions.WaitAsync(response, timeout ?? TimeSpan.FromSeconds(5)).ConfigureAwait(continueOnCapturedContext:false);
|
|
if(timeoutResponse.Success)
|
{
|
if(removed)
|
{
|
if ((bool)timeoutResponse.Result)
|
{
|
return new WaitForSelectorAsyncResponse(string.Empty, string.Empty, false);
|
}
|
|
return new WaitForSelectorAsyncResponse(false, $"Failed to detect DOM change for removed element via selector {selector}");
|
}
|
|
var element = (IDictionary<string, object>)timeoutResponse.Result;
|
var id = element["id"].ToString();
|
var tagName = element["tagName"].ToString();
|
|
return new WaitForSelectorAsyncResponse(id, tagName, true);
|
}
|
|
return new WaitForSelectorAsyncResponse(false, timeoutResponse.Message);
|
}
|
|
/// <summary>
|
/// Execute Javascript code in the context of this Browser. As the method name implies, the script will be executed
|
/// asynchronously, and the method therefore returns before the script has actually been executed. This simple helper extension
|
/// will encapsulate params in single quotes (unless int, uint, etc)
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="methodName">The javascript method name to execute.</param>
|
/// <param name="args">the arguments to be passed as params to the method. Args are encoded using
|
/// <see cref="EncodeScriptParam"/>, you can provide a custom implementation if you require one.</param>
|
public static void ExecuteScriptAsync(this IChromiumWebBrowserBase browser, string methodName, params object[] args)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.ExecuteScriptAsync(methodName, args);
|
}
|
|
/// <summary>
|
/// Execute Javascript code in the context of this WebBrowser. As the method name implies, the script will be executed
|
/// asynchronously, and the method therefore returns before the script has actually been executed. This simple helper extension
|
/// will encapsulate params in single quotes (unless int, uint, etc)
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="methodName">The javascript method name to execute.</param>
|
/// <param name="args">the arguments to be passed as params to the method. Args are encoded using
|
/// <see cref="EncodeScriptParam"/>, you can provide a custom implementation if you require one.</param>
|
public static void ExecuteScriptAsync(this IBrowser browser, string methodName, params object[] args)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var script = GetScriptForJavascriptMethodWithArgs(methodName, args);
|
|
browser.ExecuteScriptAsync(script);
|
}
|
|
/// <summary>
|
/// Execute Javascript in the context of this Browsers Main Frame. As the method name implies, the script will be executed
|
/// asynchronously, and the method therefore returns before the script has actually been executed.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
public static void ExecuteScriptAsync(this IChromiumWebBrowserBase browser, string script)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
using (var frame = browser.GetMainFrame())
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.ExecuteJavaScriptAsync(script);
|
}
|
}
|
|
/// <summary>
|
/// Execute Javascript in the context of this Browser Main Frame. As the method name implies, the script will be executed
|
/// asynchronously, and the method therefore returns before the script has actually been executed.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
public static void ExecuteScriptAsync(this IBrowser browser, string script)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.MainFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
frame.ExecuteJavaScriptAsync(script);
|
}
|
}
|
|
/// <summary>
|
/// Execute Javascript code in the context of this Browsers Main Frame. This extension method uses the LoadingStateChanged event. As the
|
/// method name implies, the script will be executed asynchronously, and the method therefore returns before the script has
|
/// actually been executed.
|
/// </summary>
|
/// <remarks>
|
/// Best effort is made to make sure the script is executed, there are likely a few edge cases where the script won't be executed,
|
/// if you suspect your script isn't being executed, then try executing in the LoadingStateChanged event handler to confirm that
|
/// it does indeed get executed.
|
/// </remarks>
|
/// <param name="webBrowser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="oneTime">(Optional) The script will only be executed on first page load, subsequent page loads will be ignored.</param>
|
public static void ExecuteScriptAsyncWhenPageLoaded(this IChromiumWebBrowserBase webBrowser, string script, bool oneTime = true)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(webBrowser);
|
|
var useLoadingStateChangedEventHandler = webBrowser.IsBrowserInitialized == false || oneTime == false;
|
|
//Browser has been initialized, we check if there is a valid document and we're not loading
|
if (webBrowser.IsBrowserInitialized)
|
{
|
//CefBrowser wrapper
|
var browser = webBrowser.BrowserCore;
|
if (browser.HasDocument && browser.IsLoading == false)
|
{
|
webBrowser.ExecuteScriptAsync(script);
|
}
|
else
|
{
|
useLoadingStateChangedEventHandler = true;
|
}
|
}
|
|
//If the browser hasn't been initialized we can just wire up the LoadingStateChanged event
|
//If the script has already been executed and oneTime is false will be hooked up next page load.
|
if (useLoadingStateChangedEventHandler)
|
{
|
EventHandler<LoadingStateChangedEventArgs> handler = null;
|
|
handler = (sender, args) =>
|
{
|
//Wait for while page to finish loading not just the first frame
|
if (!args.IsLoading)
|
{
|
if (oneTime)
|
{
|
webBrowser.LoadingStateChanged -= handler;
|
}
|
|
webBrowser.ExecuteScriptAsync(script);
|
}
|
};
|
|
webBrowser.LoadingStateChanged += handler;
|
}
|
}
|
|
/// <summary>
|
/// Creates a new instance of IRequest with the specified Url and Method = POST and then calls
|
/// <see cref="IFrame.LoadRequest(IRequest)"/>.
|
/// </summary>
|
/// <param name="browser">browser this method extends</param>
|
/// <param name="url">url to load</param>
|
/// <param name="postDataBytes">post data as byte array</param>
|
/// <param name="contentType">(Optional) if set the Content-Type header will be set</param>
|
public static void LoadUrlWithPostData(this IChromiumWebBrowserBase browser, string url, byte[] postDataBytes, string contentType = null)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.LoadUrlWithPostData(url, postDataBytes, contentType);
|
}
|
|
/// <summary>
|
/// Creates a new instance of IRequest with the specified Url and Method = POST and then calls
|
/// <see cref="IFrame.LoadRequest(IRequest)"/>.
|
/// </summary>
|
/// <param name="browser">browser this method extends</param>
|
/// <param name="url">url to load</param>
|
/// <param name="postDataBytes">post data as byte array</param>
|
/// <param name="contentType">(Optional) if set the Content-Type header will be set</param>
|
public static void LoadUrlWithPostData(this IBrowser browser, string url, byte[] postDataBytes, string contentType = null)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.MainFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
//Initialize Request with PostData
|
var request = frame.CreateRequest(initializePostData: true);
|
|
request.Url = url;
|
request.Method = "POST";
|
//Add AllowStoredCredentials as per suggestion linked in
|
//https://github.com/cefsharp/CefSharp/issues/2705#issuecomment-476819788
|
request.Flags = UrlRequestFlags.AllowStoredCredentials;
|
|
request.PostData.AddData(postDataBytes);
|
|
if (!string.IsNullOrEmpty(contentType))
|
{
|
var headers = new NameValueCollection();
|
headers.Add("Content-Type", contentType);
|
request.Headers = headers;
|
}
|
|
frame.LoadRequest(request);
|
}
|
}
|
|
/// <summary>
|
/// Registers and loads a <see cref="ResourceHandler"/> that represents the HTML content.
|
/// </summary>
|
/// <remarks>
|
/// `Cef` Native `LoadHtml` is unpredictable and only works sometimes, this method wraps the provided HTML in a
|
/// <see cref="ResourceHandler"/> and loads the provided url using the <see cref="IWebBrowser.Load"/> method. Defaults to using
|
/// <see cref="Encoding.UTF8"/> for character encoding The url must start with a valid schema, other uri's such as about:blank
|
/// are invalid A valid example looks like http://test/page.
|
/// </remarks>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="html">The HTML content.</param>
|
/// <param name="url">The URL that will be treated as the address of the content.</param>
|
/// <returns>
|
/// returns false if the Url was not successfully parsed into a Uri.
|
/// </returns>
|
public static bool LoadHtml(this IWebBrowser browser, string html, string url)
|
{
|
return browser.LoadHtml(html, url, Encoding.UTF8);
|
}
|
|
/// <summary>
|
/// Loads html as Data Uri See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs for details If
|
/// base64Encode is false then html will be Uri encoded.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="html">Html to load as data uri.</param>
|
/// <param name="base64Encode">(Optional) if true the html string will be base64 encoded using UTF8 encoding.</param>
|
public static void LoadHtml(this IChromiumWebBrowserBase browser, string html, bool base64Encode = false)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var htmlString = new HtmlString(html, base64Encode);
|
|
browser.LoadUrl(htmlString.ToDataUriString());
|
}
|
|
/// <summary>
|
/// Loads html as Data Uri See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs for details If
|
/// base64Encode is false then html will be Uri encoded.
|
/// </summary>
|
/// <param name="frame">The <seealso cref="IFrame"/> instance this method extends.</param>
|
/// <param name="html">Html to load as data uri.</param>
|
/// <param name="base64Encode">(Optional) if true the html string will be base64 encoded using UTF8 encoding.</param>
|
public static void LoadHtml(this IFrame frame, string html, bool base64Encode = false)
|
{
|
var htmlString = new HtmlString(html, base64Encode);
|
|
frame.LoadUrl(htmlString.ToDataUriString());
|
}
|
|
/// <summary>
|
/// Registers and loads a <see cref="ResourceHandler"/> that represents the HTML content.
|
/// </summary>
|
/// <remarks>
|
/// `Cef` Native `LoadHtml` is unpredictable and only works sometimes, this method wraps the provided HTML in a
|
/// <see cref="ResourceHandler"/> and loads the provided url using the <see cref="IWebBrowser.Load"/> method.
|
/// </remarks>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="html">The HTML content.</param>
|
/// <param name="url">The URL that will be treated as the address of the content.</param>
|
/// <param name="encoding">Character Encoding.</param>
|
/// <param name="oneTimeUse">(Optional) Whether or not the handler should be used once (true) or until manually unregistered
|
/// (false)</param>
|
/// <returns>
|
/// returns false if the Url was not successfully parsed into a Uri.
|
/// </returns>
|
public static bool LoadHtml(this IWebBrowser browser, string html, string url, Encoding encoding, bool oneTimeUse = false)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
if (browser.ResourceRequestHandlerFactory == null)
|
{
|
browser.ResourceRequestHandlerFactory = new ResourceRequestHandlerFactory();
|
}
|
|
var handler = browser.ResourceRequestHandlerFactory as ResourceRequestHandlerFactory;
|
|
if (handler == null)
|
{
|
throw new Exception("LoadHtml can only be used with the default IResourceRequestHandlerFactory(DefaultResourceRequestHandlerFactory) implementation");
|
}
|
|
if (handler.RegisterHandler(url, ResourceHandler.GetByteArray(html, encoding, true), ResourceHandler.DefaultMimeType, oneTimeUse))
|
{
|
browser.Load(url);
|
return true;
|
}
|
return false;
|
}
|
|
/// <summary>
|
/// Register a ResourceHandler. Can only be used when browser.ResourceHandlerFactory is an instance of
|
/// DefaultResourceHandlerFactory.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="url">the url of the resource to unregister.</param>
|
/// <param name="stream">Stream to be registered, the stream should not be shared with any other instances of
|
/// DefaultResourceHandlerFactory.</param>
|
/// <param name="mimeType">(Optional) the mimeType.</param>
|
/// <param name="oneTimeUse">(Optional) Whether or not the handler should be used once (true) or until manually unregistered
|
/// (false). If true the Stream will be Diposed of when finished.</param>
|
public static void RegisterResourceHandler(this IWebBrowser browser, string url, Stream stream, string mimeType = ResourceHandler.DefaultMimeType,
|
bool oneTimeUse = false)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
if (browser.ResourceRequestHandlerFactory == null)
|
{
|
browser.ResourceRequestHandlerFactory = new ResourceRequestHandlerFactory();
|
}
|
|
var handler = browser.ResourceRequestHandlerFactory as ResourceRequestHandlerFactory;
|
|
if (handler == null)
|
{
|
throw new Exception("RegisterResourceHandler can only be used with the default IResourceRequestHandlerFactory(DefaultResourceRequestHandlerFactory) implementation");
|
}
|
|
using (var ms = new MemoryStream())
|
{
|
stream.CopyTo(ms);
|
|
handler.RegisterHandler(url, ms.ToArray(), mimeType, oneTimeUse);
|
}
|
}
|
|
/// <summary>
|
/// Unregister a ResourceHandler. Can only be used when browser.ResourceHandlerFactory is an instance of
|
/// DefaultResourceHandlerFactory.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="url">the url of the resource to unregister.</param>
|
public static void UnRegisterResourceHandler(this IWebBrowser browser, string url)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var handler = browser.ResourceRequestHandlerFactory as ResourceRequestHandlerFactory;
|
|
if (handler == null)
|
{
|
throw new Exception("UnRegisterResourceHandler can only be used with the default IResourceRequestHandlerFactory(DefaultResourceRequestHandlerFactory) implementation");
|
}
|
|
handler.UnregisterHandler(url);
|
}
|
|
/// <summary>
|
/// Stops loading the current page.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Stop(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Stop();
|
}
|
|
/// <summary>
|
/// Stops loading the current page.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Stop(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
browser.StopLoad();
|
}
|
|
/// <summary>
|
/// Navigates back, must check <see cref="IChromiumWebBrowserBase.CanGoBack"/> before calling this method.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Back(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Back();
|
}
|
|
/// <summary>
|
/// Navigates back, must check <see cref="IBrowser.CanGoBack"/> before calling this method.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Back(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
browser.GoBack();
|
}
|
|
/// <summary>
|
/// Navigates forward, must check <see cref="IChromiumWebBrowserBase.CanGoForward"/> before calling this method.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Forward(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Forward();
|
}
|
|
/// <summary>
|
/// Navigates forward, must check <see cref="IBrowser.CanGoForward"/> before calling this method.
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
public static void Forward(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
browser.GoForward();
|
}
|
|
/// <summary>
|
/// Reloads the page being displayed. This method will use data from the browser's cache, if available.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Reload(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.Reload(false);
|
}
|
|
/// <summary>
|
/// Reloads the page being displayed, optionally ignoring the cache (which means the whole page including all .css, .js etc.
|
/// resources will be re-fetched).
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="ignoreCache"><c>true</c> A reload is performed ignoring browser cache; <c>false</c> A reload is performed using
|
/// files from the browser cache, if available.</param>
|
public static void Reload(this IChromiumWebBrowserBase browser, bool ignoreCache)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Reload(ignoreCache);
|
}
|
|
/// <summary>
|
/// Reloads the page being displayed, optionally ignoring the cache (which means the whole page including all .css, .js etc.
|
/// resources will be re-fetched).
|
/// </summary>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="ignoreCache"><c>true</c> A reload is performed ignoring browser cache; <c>false</c> A reload is performed using
|
/// files from the browser cache, if available.</param>
|
public static void Reload(this IBrowser browser, bool ignoreCache = false)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
browser.Reload(ignoreCache);
|
}
|
|
/// <summary>
|
/// Gets the default cookie manager associated with the <see cref="IChromiumWebBrowserBase"/> instance.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="callback">(Optional) If not null it will be executed asynchronously on the CEF IO thread after the manager's
|
/// storage has been initialized.</param>
|
/// <returns>
|
/// Cookie Manager.
|
/// </returns>
|
public static ICookieManager GetCookieManager(this IChromiumWebBrowserBase browser, ICompletionCallback callback = null)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var host = browser.GetBrowserHost();
|
|
ThrowExceptionIfBrowserHostNull(host);
|
|
var requestContext = host.RequestContext;
|
|
if (requestContext == null)
|
{
|
throw new Exception("RequestContext is null, unable to obtain cookie manager");
|
}
|
|
return requestContext.GetCookieManager(callback);
|
}
|
|
/// <summary>
|
/// Gets the RequestContext associated with the <see cref="IChromiumWebBrowserBase"/> instance.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// RequestContext
|
/// </returns>
|
public static IRequestContext GetRequestContext(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var host = browser.GetBrowserHost();
|
|
ThrowExceptionIfBrowserHostNull(host);
|
|
return host.RequestContext;
|
}
|
|
/// <summary>
|
/// Asynchronously gets the current Zoom Level.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// An asynchronous result that yields the zoom level.
|
/// </returns>
|
public static Task<double> GetZoomLevelAsync(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
return host.GetZoomLevelAsync();
|
}
|
|
/// <summary>
|
/// Asynchronously gets the current Zoom Level.
|
/// </summary>
|
/// <param name="browser">the ChromiumWebBrowser instance.</param>
|
/// <returns>
|
/// An asynchronous result that yields the zoom level.
|
/// </returns>
|
public static Task<double> GetZoomLevelAsync(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
return browser.BrowserCore.GetZoomLevelAsync();
|
}
|
|
/// <summary>
|
/// Change the ZoomLevel to the specified value. Can be set to 0.0 to clear the zoom level.
|
/// </summary>
|
/// <remarks>
|
/// If called on the CEF UI thread the change will be applied immediately. Otherwise, the change will be applied asynchronously
|
/// on the CEF UI thread. The CEF UI thread is different to the WPF/WinForms UI Thread.
|
/// </remarks>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="zoomLevel">zoom level.</param>
|
public static void SetZoomLevel(this IBrowser browser, double zoomLevel)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.SetZoomLevel(zoomLevel);
|
}
|
|
/// <summary>
|
/// Change the ZoomLevel to the specified value. Can be set to 0.0 to clear the zoom level.
|
/// </summary>
|
/// <remarks>
|
/// If called on the CEF UI thread the change will be applied immediately. Otherwise, the change will be applied asynchronously
|
/// on the CEF UI thread. The CEF UI thread is different to the WPF/WinForms UI Thread.
|
/// </remarks>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="zoomLevel">zoom level.</param>
|
public static void SetZoomLevel(this IChromiumWebBrowserBase browser, double zoomLevel)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.SetZoomLevel(zoomLevel);
|
}
|
|
/// <summary>
|
/// Search for text within the current page.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="searchText">text to search for</param>
|
/// <param name="forward">indicates whether to search forward or backward within the page</param>
|
/// <param name="matchCase">indicates whether the search should be case-sensitive</param>
|
/// <param name="findNext">indicates whether this is the first request or a follow-up</param>
|
/// <remarks>The <see cref="IFindHandler"/> instance, if any, will be called to report find results.</remarks>
|
public static void Find(this IBrowser browser, string searchText, bool forward, bool matchCase, bool findNext)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.Find(searchText, forward, matchCase, findNext);
|
}
|
|
/// <summary>
|
/// Search for text within the current page.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="searchText">text to search for</param>
|
/// <param name="forward">indicates whether to search forward or backward within the page</param>
|
/// <param name="matchCase">indicates whether the search should be case-sensitive</param>
|
/// <param name="findNext">indicates whether this is the first request or a follow-up</param>
|
/// <remarks>The <see cref="IFindHandler"/> instance, if any, will be called to report find results.</remarks>
|
public static void Find(this IChromiumWebBrowserBase browser, string searchText, bool forward, bool matchCase, bool findNext)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Find(searchText, forward, matchCase, findNext);
|
}
|
|
/// <summary>
|
/// Cancel all searches that are currently going on.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="clearSelection">clear the current search selection.</param>
|
public static void StopFinding(this IBrowser browser, bool clearSelection)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.StopFinding(clearSelection);
|
}
|
|
/// <summary>
|
/// Cancel all searches that are currently going on.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="clearSelection">clear the current search selection.</param>
|
public static void StopFinding(this IChromiumWebBrowserBase browser, bool clearSelection)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.StopFinding(clearSelection);
|
}
|
|
/// <summary>
|
/// Opens a Print Dialog which if used (can be user cancelled) will print the browser contents.
|
/// </summary>
|
/// <param name="browser">The browser instance this method extends.</param>
|
public static void Print(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.Print();
|
}
|
|
/// <summary>
|
/// Opens a Print Dialog which if used (can be user cancelled) will print the browser contents.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void Print(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.Print();
|
}
|
|
/// <summary>
|
/// Asynchronously prints the current browser contents to the PDF file specified. The caller is responsible for deleting the file
|
/// when done.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> object this method extends.</param>
|
/// <param name="path">Output file location.</param>
|
/// <param name="settings">(Optional) Print Settings.</param>
|
/// <returns>
|
/// A task that represents the asynchronous print operation. The result is true on success or false on failure to generate the
|
/// Pdf.
|
/// </returns>
|
public static Task<bool> PrintToPdfAsync(this IBrowser browser, string path, PdfPrintSettings settings = null)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
var callback = new TaskPrintToPdfCallback();
|
host.PrintToPdf(path, settings, callback);
|
|
return callback.Task;
|
}
|
|
/// <summary>
|
/// Asynchronously prints the current browser contents to the PDF file specified. The caller is responsible for deleting the file
|
/// when done.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="path">Output file location.</param>
|
/// <param name="settings">(Optional) Print Settings.</param>
|
/// <returns>
|
/// A task that represents the asynchronous print operation. The result is true on success or false on failure to generate the
|
/// Pdf.
|
/// </returns>
|
public static Task<bool> PrintToPdfAsync(this IChromiumWebBrowserBase browser, string path, PdfPrintSettings settings = null)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
return browser.BrowserCore.PrintToPdfAsync(path, settings);
|
}
|
|
/// <summary>
|
/// Open developer tools in its own window.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="windowInfo">(Optional) window info used for showing dev tools.</param>
|
/// <param name="inspectElementAtX">(Optional) x coordinate (used for inspectElement)</param>
|
/// <param name="inspectElementAtY">(Optional) y coordinate (used for inspectElement)</param>
|
public static void ShowDevTools(this IBrowser browser, IWindowInfo windowInfo = null, int inspectElementAtX = 0, int inspectElementAtY = 0)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.ShowDevTools(windowInfo, inspectElementAtX, inspectElementAtY);
|
}
|
|
/// <summary>
|
/// Open developer tools in its own window.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="windowInfo">(Optional) window info used for showing dev tools.</param>
|
/// <param name="inspectElementAtX">(Optional) x coordinate (used for inspectElement)</param>
|
/// <param name="inspectElementAtY">(Optional) y coordinate (used for inspectElement)</param>
|
public static void ShowDevTools(this IChromiumWebBrowserBase browser, IWindowInfo windowInfo = null, int inspectElementAtX = 0, int inspectElementAtY = 0)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.ShowDevTools(windowInfo, inspectElementAtX, inspectElementAtY);
|
}
|
|
/// <summary>
|
/// Explicitly close the developer tools window if one exists for this browser instance.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
public static void CloseDevTools(this IBrowser browser)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.CloseDevTools();
|
}
|
|
/// <summary>
|
/// Explicitly close the developer tools window if one exists for this browser instance.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void CloseDevTools(this IChromiumWebBrowserBase browser)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.CloseDevTools();
|
}
|
|
/// <summary>
|
/// If a misspelled word is currently selected in an editable node calling this method will replace it with the specified word.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="word">The new word that will replace the currently selected word.</param>
|
public static void ReplaceMisspelling(this IBrowser browser, string word)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.ReplaceMisspelling(word);
|
}
|
|
/// <summary>
|
/// If a misspelled word is currently selected in an editable node calling this method will replace it with the specified word.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="word">The new word that will replace the currently selected word.</param>
|
public static void ReplaceMisspelling(this IChromiumWebBrowserBase browser, string word)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.ReplaceMisspelling(word);
|
}
|
|
/// <summary>
|
/// Add the specified word to the spelling dictionary.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="word">The new word that will be added to the dictionary.</param>
|
public static void AddWordToDictionary(this IBrowser browser, string word)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.AddWordToDictionary(word);
|
}
|
|
/// <summary>
|
/// Add the specified word to the spelling dictionary.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="word">The new word that will be added to the dictionary.</param>
|
public static void AddWordToDictionary(this IChromiumWebBrowserBase browser, string word)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.AddWordToDictionary(word);
|
}
|
|
/// <summary>
|
/// Shortcut method to get the browser IBrowserHost.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// browserHost or null.
|
/// </returns>
|
public static IBrowserHost GetBrowserHost(this IChromiumWebBrowserBase browser)
|
{
|
return browser.BrowserCore?.GetHost();
|
}
|
|
/// <summary>
|
/// Send a mouse wheel event to the browser.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="x">The x coordinate relative to upper-left corner of view.</param>
|
/// <param name="y">The y coordinate relative to upper-left corner of view.</param>
|
/// <param name="deltaX">The delta x coordinate.</param>
|
/// <param name="deltaY">The delta y coordinate.</param>
|
/// <param name="modifiers">The modifiers.</param>
|
public static void SendMouseWheelEvent(this IChromiumWebBrowserBase browser, int x, int y, int deltaX, int deltaY, CefEventFlags modifiers)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
browser.BrowserCore.SendMouseWheelEvent(x, y, deltaX, deltaY, modifiers);
|
}
|
|
/// <summary>
|
/// Send a mouse wheel event to the browser.
|
/// </summary>
|
/// <param name="browser">The <see cref="IBrowser"/> instance this method extends.</param>
|
/// <param name="x">The x coordinate relative to upper-left corner of view.</param>
|
/// <param name="y">The y coordinate relative to upper-left corner of view.</param>
|
/// <param name="deltaX">The delta x coordinate.</param>
|
/// <param name="deltaY">The delta y coordinate.</param>
|
/// <param name="modifiers">The modifiers.</param>
|
public static void SendMouseWheelEvent(this IBrowser browser, int x, int y, int deltaX, int deltaY, CefEventFlags modifiers)
|
{
|
ThrowExceptionIfBrowserNull(browser);
|
|
var host = browser.GetHost();
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.SendMouseWheelEvent(new MouseEvent(x, y, modifiers), deltaX, deltaY);
|
}
|
|
/// <summary>
|
/// Send a mouse wheel event to the browser.
|
/// </summary>
|
/// <param name="host">browserHost.</param>
|
/// <param name="x">The x coordinate relative to upper-left corner of view.</param>
|
/// <param name="y">The y coordinate relative to upper-left corner of view.</param>
|
/// <param name="deltaX">The delta x coordinate.</param>
|
/// <param name="deltaY">The delta y coordinate.</param>
|
/// <param name="modifiers">The modifiers.</param>
|
public static void SendMouseWheelEvent(this IBrowserHost host, int x, int y, int deltaX, int deltaY, CefEventFlags modifiers)
|
{
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.SendMouseWheelEvent(new MouseEvent(x, y, modifiers), deltaX, deltaY);
|
}
|
|
/// <summary>
|
/// Send a mouse click event to the browser.
|
/// </summary>
|
/// <param name="host">browserHost.</param>
|
/// <param name="x">The x coordinate relative to upper-left corner of view.</param>
|
/// <param name="y">The y coordinate relative to upper-left corner of view.</param>
|
/// <param name="mouseButtonType">Type of the mouse button.</param>
|
/// <param name="mouseUp">True to mouse up.</param>
|
/// <param name="clickCount">Number of clicks.</param>
|
/// <param name="modifiers">The modifiers.</param>
|
public static void SendMouseClickEvent(this IBrowserHost host, int x, int y, MouseButtonType mouseButtonType, bool mouseUp, int clickCount, CefEventFlags modifiers)
|
{
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.SendMouseClickEvent(new MouseEvent(x, y, modifiers), mouseButtonType, mouseUp, clickCount);
|
}
|
|
/// <summary>
|
/// Send a mouse move event to the browser.
|
/// </summary>
|
/// <param name="host">browserHost.</param>
|
/// <param name="x">The x coordinate relative to upper-left corner of view.</param>
|
/// <param name="y">The y coordinate relative to upper-left corner of view.</param>
|
/// <param name="mouseLeave">mouse leave.</param>
|
/// <param name="modifiers">The modifiers.</param>
|
public static void SendMouseMoveEvent(this IBrowserHost host, int x, int y, bool mouseLeave, CefEventFlags modifiers)
|
{
|
ThrowExceptionIfBrowserHostNull(host);
|
|
host.SendMouseMoveEvent(new MouseEvent(x, y, modifiers), mouseLeave);
|
}
|
|
/// <summary>
|
/// Evaluate Javascript in the context of the MainFrame of the ChromiumWebBrowser. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript. The result of the script execution
|
/// in javascript is Promise.resolve so even no promise values will be treated as a promise. Your javascript should return a value.
|
/// The javascript will be wrapped in an Immediately Invoked Function Expression.
|
/// When the promise either trigger then/catch this returned Task will be completed.
|
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <param name="chromiumWebBrowser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to perform the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsPromiseAsync(this IWebBrowser chromiumWebBrowser, string script, TimeSpan? timeout = null)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser);
|
|
var jsbSettings = chromiumWebBrowser.JavascriptObjectRepository.Settings;
|
|
var promiseHandlerScript = GetPromiseHandlerScript(script, jsbSettings.JavascriptBindingApiGlobalObjectName);
|
|
return chromiumWebBrowser.EvaluateScriptAsync(promiseHandlerScript, timeout: timeout, useImmediatelyInvokedFuncExpression: true);
|
}
|
|
/// <summary>
|
/// Evaluate Javascript in the context of this Browsers Main Frame. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript. The result of the script execution
|
/// in javascript is Promise.resolve so even no promise values will be treated as a promise. Your javascript should return a value.
|
/// The javascript will be wrapped in an Immediately Invoked Function Expression.
|
/// When the promise either trigger then/catch this returned Task will be completed.
|
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to perform the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsPromiseAsync(this IBrowser browser, string script, TimeSpan? timeout = null)
|
{
|
var promiseHandlerScript = GetPromiseHandlerScript(script, null);
|
|
return browser.EvaluateScriptAsync(promiseHandlerScript, timeout: timeout, useImmediatelyInvokedFuncExpression: true);
|
}
|
|
/// <summary>
|
/// Evaluate Javascript in the context of this Browsers Main Frame. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript. The result of the script execution
|
/// in javascript is Promise.resolve so even no promise values will be treated as a promise. Your javascript should return a value.
|
/// The javascript will be wrapped in an Immediately Invoked Function Expression.
|
/// When the promise either trigger then/catch this returned Task will be completed.
|
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <param name="frame">The <seealso cref="IFrame"/> instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
|
/// <param name="javascriptBindingApiGlobalObjectName">
|
/// Only required if a custom value was specified for <see cref="JavascriptBinding.JavascriptBindingSettings.JavascriptBindingApiGlobalObjectName"/>
|
/// then this param must match that value. Otherwise exclude passing a value for this param or pass in null.
|
/// </param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to perform the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsPromiseAsync(this IFrame frame, string script, TimeSpan? timeout = null, string javascriptBindingApiGlobalObjectName = null)
|
{
|
var promiseHandlerScript = GetPromiseHandlerScript(script, javascriptBindingApiGlobalObjectName);
|
|
return frame.EvaluateScriptAsync(promiseHandlerScript, timeout: timeout, useImmediatelyInvokedFuncExpression: true);
|
}
|
|
private static string GetPromiseHandlerScript(string script, string javascriptBindingApiGlobalObjectName)
|
{
|
var internalJsFunctionName = "cefSharp.sendEvalScriptResponse";
|
|
//If the user chose to customise the name of the object CefSharp
|
//creates in Javascript then we'll workout what the name should be.
|
if (!string.IsNullOrWhiteSpace(javascriptBindingApiGlobalObjectName))
|
{
|
internalJsFunctionName = javascriptBindingApiGlobalObjectName;
|
|
if (char.IsLower(internalJsFunctionName[0]))
|
{
|
internalJsFunctionName += ".sendEvalScriptResponse";
|
}
|
else
|
{
|
internalJsFunctionName += ".SendEvalScriptResponse";
|
}
|
}
|
var promiseHandlerScript = "let innerImmediatelyInvokedFuncExpression = (async function() { " + script + " })(); Promise.resolve(innerImmediatelyInvokedFuncExpression).then((val) => " + internalJsFunctionName + "(cefSharpInternalCallbackId, true, val, false)).catch ((reason) => " + internalJsFunctionName + "(cefSharpInternalCallbackId, false, String(reason), false)); return 'CefSharpDefEvalScriptRes';";
|
|
return promiseHandlerScript;
|
}
|
|
/// <summary>
|
/// Evaluate Javascript in the context of this Browsers Main Frame. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript
|
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
|
/// <param name="useImmediatelyInvokedFuncExpression">When true the script is wrapped in a self executing function.
|
/// Make sure to use a return statement in your javascript. e.g. (function () { return 42; })();
|
/// When false don't include a return statement e.g. 42;
|
/// </param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to obtain the result of the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsync(this IChromiumWebBrowserBase browser, string script, TimeSpan? timeout = null, bool useImmediatelyInvokedFuncExpression = false)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
if (browser is IWebBrowser b)
|
{
|
if (b.CanExecuteJavascriptInMainFrame == false)
|
{
|
ThrowExceptionIfCanExecuteJavascriptInMainFrameFalse();
|
}
|
}
|
|
return browser.BrowserCore.EvaluateScriptAsync(script, timeout, useImmediatelyInvokedFuncExpression);
|
}
|
|
/// <summary>
|
/// Evaluate some Javascript code in the context of the MainFrame of the ChromiumWebBrowser. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript
|
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <param name="browser">The IBrowser instance this method extends.</param>
|
/// <param name="script">The Javascript code that should be executed.</param>
|
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
|
/// <param name="useImmediatelyInvokedFuncExpression">When true the script is wrapped in a self executing function.
|
/// Make sure to use a return statement in your javascript. e.g. (function () { return 42; })();
|
/// When false don't include a return statement e.g. 42;
|
/// </param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to obtain the result of the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsync(this IBrowser browser, string script, TimeSpan? timeout = null, bool useImmediatelyInvokedFuncExpression = false)
|
{
|
if (timeout.HasValue && timeout.Value.TotalMilliseconds > UInt32.MaxValue)
|
{
|
throw new ArgumentOutOfRangeException("timeout", "Timeout greater than Maximum allowable value of " + UInt32.MaxValue);
|
}
|
|
ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.MainFrame)
|
{
|
ThrowExceptionIfFrameNull(frame);
|
|
return frame.EvaluateScriptAsync(script, timeout: timeout, useImmediatelyInvokedFuncExpression: useImmediatelyInvokedFuncExpression);
|
}
|
}
|
|
/// <summary>
|
/// Evaluate some Javascript code in the context of this WebBrowser. The script will be executed asynchronously and the method
|
/// returns a Task encapsulating the response from the Javascript This simple helper extension will encapsulate params in single
|
/// quotes (unless int, uint, etc)
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="methodName">The javascript method name to execute.</param>
|
/// <param name="args">the arguments to be passed as params to the method.</param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to obtain the result of the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsync(this IChromiumWebBrowserBase browser, string methodName, params object[] args)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
return browser.EvaluateScriptAsync(null, methodName, args);
|
}
|
|
/// <summary>
|
/// Evaluate Javascript code in the context of this WebBrowser using the specified timeout. The script will be executed
|
/// asynchronously and the method returns a Task encapsulating the response from the Javascript This simple helper extension will
|
/// encapsulate params in single quotes (unless int, uint, etc).
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <param name="timeout">The timeout after which the Javascript code execution should be aborted.</param>
|
/// <param name="methodName">The javascript method name to execute.</param>
|
/// <param name="args">the arguments to be passed as params to the method. Args are encoded using
|
/// <see cref="EncodeScriptParam"/>, you can provide a custom implementation if you require a custom implementation.</param>
|
/// <returns>
|
/// <see cref="Task{JavascriptResponse}"/> that can be awaited to perform the script execution.
|
/// </returns>
|
public static Task<JavascriptResponse> EvaluateScriptAsync(this IChromiumWebBrowserBase browser, TimeSpan? timeout, string methodName, params object[] args)
|
{
|
ThrowExceptionIfChromiumWebBrowserDisposed(browser);
|
|
var script = GetScriptForJavascriptMethodWithArgs(methodName, args);
|
|
return browser.EvaluateScriptAsync(script, timeout);
|
}
|
|
/// <summary>
|
/// An IWebBrowser extension method that sets the <see cref="IWebBrowserInternal.HasParent"/>
|
/// property used when passing a ChromiumWebBrowser instance to <see cref="ILifeSpanHandler.OnBeforePopup"/>
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void SetAsPopup(this IWebBrowser browser)
|
{
|
var internalBrowser = (IWebBrowserInternal)browser;
|
|
internalBrowser.HasParent = true;
|
}
|
|
/// <summary>
|
/// Dispose of the DevToolsContext (if any). Used in conjunction with CefSharp.Dom
|
/// </summary>
|
/// <param name="webBrowserInternal">ChromiumWebBrowser instance</param>
|
public static void DisposeDevToolsContext(this IWebBrowserInternal webBrowserInternal)
|
{
|
if(webBrowserInternal == null)
|
{
|
return;
|
}
|
|
webBrowserInternal.DevToolsContext?.Dispose();
|
webBrowserInternal.DevToolsContext = null;
|
}
|
|
/// <summary>
|
/// Set the <see cref="IWebBrowserInternal.DevToolsContext"/> property to null. Used in conjunction with CefSharp.Dom
|
/// </summary>
|
/// <param name="webBrowserInternal">ChromiumWebBrowser instance</param>
|
public static void FreeDevToolsContext(this IWebBrowserInternal webBrowserInternal)
|
{
|
if (webBrowserInternal == null)
|
{
|
return;
|
}
|
|
webBrowserInternal.DevToolsContext = null;
|
}
|
|
/// <summary>
|
/// Function used to encode the params passed to <see cref="ExecuteScriptAsync(IWebBrowser, string, object[])"/>,
|
/// <see cref="EvaluateScriptAsync(IWebBrowser, string, object[])"/> and
|
/// <see cref="EvaluateScriptAsync(IWebBrowser, TimeSpan?, string, object[])"/>
|
/// Provide your own custom function to perform custom encoding. You can use your choice of JSON encoder here if you should so
|
/// choose.
|
/// </summary>
|
/// <value>
|
/// A function delegate that yields a string.
|
/// </value>
|
public static Func<string, string> EncodeScriptParam { get; set; } = (str) =>
|
{
|
return str.Replace("\\", "\\\\")
|
.Replace("'", "\\'")
|
.Replace("\t", "\\t")
|
.Replace("\r", "\\r")
|
.Replace("\n", "\\n");
|
};
|
|
/// <summary>
|
/// Checks if the given object is a numerical object.
|
/// </summary>
|
/// <param name="value">The object to check.</param>
|
/// <returns>
|
/// True if numeric, otherwise false.
|
/// </returns>
|
private static bool IsNumeric(this object value)
|
{
|
return value is sbyte
|
|| value is byte
|
|| value is short
|
|| value is ushort
|
|| value is int
|
|| value is uint
|
|| value is long
|
|| value is ulong
|
|| value is float
|
|| value is double
|
|| value is decimal;
|
}
|
|
/// <summary>
|
/// Transforms the methodName and arguments into valid Javascript code. Will encapsulate params in single quotes (unless int,
|
/// uint, etc)
|
/// </summary>
|
/// <param name="methodName">The javascript method name to execute.</param>
|
/// <param name="args">the arguments to be passed as params to the method.</param>
|
/// <returns>
|
/// The Javascript code.
|
/// </returns>
|
public static string GetScriptForJavascriptMethodWithArgs(string methodName, object[] args)
|
{
|
var stringBuilder = new StringBuilder();
|
stringBuilder.Append(methodName);
|
stringBuilder.Append("(");
|
|
if (args.Length > 0)
|
{
|
for (int i = 0; i < args.Length; i++)
|
{
|
var obj = args[i];
|
if (obj == null)
|
{
|
stringBuilder.Append("null");
|
}
|
else if (obj.IsNumeric())
|
{
|
stringBuilder.Append(Convert.ToString(args[i], CultureInfo.InvariantCulture));
|
}
|
else if (obj is bool)
|
{
|
stringBuilder.Append(args[i].ToString().ToLowerInvariant());
|
}
|
else
|
{
|
stringBuilder.Append("'");
|
stringBuilder.Append(EncodeScriptParam(obj.ToString()));
|
stringBuilder.Append("'");
|
}
|
|
stringBuilder.Append(", ");
|
}
|
|
//Remove the trailing comma
|
stringBuilder.Remove(stringBuilder.Length - 2, 2);
|
}
|
|
stringBuilder.Append(");");
|
|
return stringBuilder.ToString();
|
}
|
|
public static void ThrowExceptionIfChromiumWebBrowserDisposed(IChromiumWebBrowserBase browser)
|
{
|
if (browser == null)
|
{
|
throw new ArgumentNullException(nameof(browser));
|
}
|
|
if (browser.IsDisposed)
|
{
|
// Provide a more meaningful message for WinForms ChromiumHostControl
|
// should be ChromiumWebBrowser/ChromiumHostControl
|
var type = browser.GetType();
|
|
throw new ObjectDisposedException(type.Name);
|
}
|
}
|
|
/// <summary>
|
/// Throw exception if frame null.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="frame">The <seealso cref="IFrame"/> instance this method extends.</param>
|
public static void ThrowExceptionIfFrameNull(IFrame frame)
|
{
|
if (frame == null)
|
{
|
throw new Exception(FrameNullExceptionString);
|
}
|
}
|
|
/// <summary>
|
/// An IBrowser extension method that throw exception if browser null.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void ThrowExceptionIfBrowserNull(IBrowser browser)
|
{
|
if (browser == null)
|
{
|
throw new Exception(BrowserNullExceptionString);
|
}
|
}
|
|
/// <summary>
|
/// Throw exception if browser host null.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
/// <param name="browserHost">The browser host.</param>
|
public static void ThrowExceptionIfBrowserHostNull(IBrowserHost browserHost)
|
{
|
if (browserHost == null)
|
{
|
throw new Exception(BrowserHostNullExceptionString);
|
}
|
}
|
|
/// <summary>
|
/// Throw exception if can execute javascript in main frame false.
|
/// </summary>
|
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
|
public static void ThrowExceptionIfCanExecuteJavascriptInMainFrameFalse()
|
{
|
throw new Exception("Unable to execute javascript at this time, scripts can only be executed within a V8Context. " +
|
"Use the IWebBrowser.CanExecuteJavascriptInMainFrame property to guard against this exception. " +
|
"See https://github.com/cefsharp/CefSharp/wiki/General-Usage#when-can-i-start-executing-javascript " +
|
"for more details on when you can execute javascript. For frames that do not contain Javascript then no " +
|
"V8Context will be created. Executing a script once the frame has loaded it's possible to create a V8Context. " +
|
"You can use browser.GetMainFrame().ExecuteJavaScriptAsync(script) or browser.GetMainFrame().EvaluateScriptAsync " +
|
"to bypass these checks (advanced users only).");
|
}
|
}
|
}
|