// Copyright © 2021 The CefSharp Authors. All rights reserved.
|
//
|
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
|
using CefSharp.Internals;
|
using CefSharp.ModelBinding;
|
using System;
|
using System.IO;
|
using System.Threading.Tasks;
|
|
namespace CefSharp
|
{
|
/// <summary>
|
/// Extended WebBrowserExtensions
|
/// </summary>
|
public static class WebBrowserExtensionsEx
|
{
|
/// <summary>
|
/// Retrieve the current <see cref="NavigationEntry"/>. Contains information like
|
/// <see cref="NavigationEntry.HttpStatusCode"/> and <see cref="NavigationEntry.SslStatus"/>
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
/// <returns>
|
/// <see cref="Task{NavigationEntry}"/> that when executed returns the current <see cref="NavigationEntry"/> or null
|
/// </returns>
|
public static Task<NavigationEntry> GetVisibleNavigationEntryAsync(this IChromiumWebBrowserBase browser)
|
{
|
var host = browser.GetBrowserHost();
|
|
if (host == null)
|
{
|
return Task.FromResult<NavigationEntry>(null);
|
}
|
|
if(Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
|
{
|
var entry = host.GetVisibleNavigationEntry();
|
|
return Task.FromResult<NavigationEntry>(entry);
|
}
|
|
var tcs = new TaskCompletionSource<NavigationEntry>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
Cef.UIThreadTaskFactory.StartNew(delegate
|
{
|
var entry = host.GetVisibleNavigationEntry();
|
|
tcs.TrySetResult(entry);
|
});
|
|
return tcs.Task;
|
}
|
|
/// <summary>
|
/// Downloads the specified <paramref name="url"/> and calls <paramref name="completeHandler"/>
|
/// when the download is complete. Makes a GET Request.
|
/// </summary>
|
/// <param name="frame">valid frame</param>
|
/// <param name="url">url to download</param>
|
/// <param name="completeHandler">Action to be executed when the download is complete.</param>
|
public static void DownloadUrl(this IFrame frame, string url, Action<IUrlRequest, Stream> completeHandler)
|
{
|
if (!frame.IsValid)
|
{
|
throw new Exception("Frame is invalid, unable to continue.");
|
}
|
|
//Can be created on any valid CEF Thread, here we'll use the CEF UI Thread
|
Cef.UIThreadTaskFactory.StartNew(delegate
|
{
|
var request = frame.CreateRequest(false);
|
|
request.Method = "GET";
|
request.Url = url;
|
|
var memoryStream = new MemoryStream();
|
|
var urlRequestClient = Fluent.UrlRequestClient
|
.Create()
|
.OnDownloadData((req, stream) =>
|
{
|
stream.CopyTo(memoryStream);
|
})
|
.OnRequestComplete((req) =>
|
{
|
memoryStream.Position = 0;
|
|
completeHandler?.Invoke(req, memoryStream);
|
})
|
.Build();
|
|
var urlRequest = frame.CreateUrlRequest(request, urlRequestClient);
|
});
|
}
|
|
/// <summary>
|
/// Downloads the specified <paramref name="url"/> as a <see cref="T:byte[]"/>.
|
/// Makes a GET Request.
|
/// </summary>
|
/// <param name="frame">valid frame</param>
|
/// <param name="url">url to download</param>
|
/// <param name="urlRequestFlags">control caching policy</param>
|
/// <returns>A task that can be awaited to get the <see cref="T:byte[]"/> representing the Url</returns>
|
public static Task<byte[]> DownloadUrlAsync(this IFrame frame, string url, UrlRequestFlags urlRequestFlags = UrlRequestFlags.None)
|
{
|
if (frame == null)
|
{
|
throw new ArgumentNullException(nameof(frame));
|
}
|
|
if (!frame.IsValid)
|
{
|
throw new Exception("Frame is invalid, unable to continue.");
|
}
|
|
var taskCompletionSource = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
//Can be created on any valid CEF Thread, here we'll use the CEF UI Thread
|
Cef.UIThreadTaskFactory.StartNew(delegate
|
{
|
var request = frame.CreateRequest(false);
|
|
request.Method = "GET";
|
request.Url = url;
|
request.Flags = urlRequestFlags;
|
|
var memoryStream = new MemoryStream();
|
|
var urlRequestClient = Fluent.UrlRequestClient
|
.Create()
|
.OnDownloadData((req, stream) =>
|
{
|
stream.CopyTo(memoryStream);
|
})
|
.OnRequestComplete((req) =>
|
{
|
if (req.RequestStatus == UrlRequestStatus.Success)
|
{
|
taskCompletionSource.TrySetResult(memoryStream.ToArray());
|
}
|
else
|
{
|
taskCompletionSource.TrySetException(new Exception("RequestStatus:" + req.RequestStatus + ";StatusCode:" + req.Response.StatusCode));
|
}
|
})
|
.Build();
|
|
var urlRequest = frame.CreateUrlRequest(request, urlRequestClient);
|
});
|
|
return taskCompletionSource.Task;
|
}
|
|
/// <summary>
|
/// Toggles audio mute for the current browser.
|
/// If the <paramref name="browser"/> is null or has been disposed
|
/// then this command will be a no-op.
|
/// </summary>
|
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
|
public static void ToggleAudioMute(this IChromiumWebBrowserBase browser)
|
{
|
if (browser.IsDisposed || Cef.IsShutdown)
|
{
|
return;
|
}
|
|
_ = Cef.UIThreadTaskFactory.StartNew(delegate
|
{
|
var cefBrowser = browser.BrowserCore;
|
|
if (cefBrowser == null || cefBrowser.IsDisposed)
|
{
|
return;
|
}
|
|
var host = cefBrowser.GetHost();
|
|
var isAudioMuted = host.IsAudioMuted;
|
|
host.SetAudioMuted(!isAudioMuted);
|
});
|
}
|
|
/// <summary>
|
/// Evaluate javascript code in the context of the <paramref name="frame"/>. The script will be executed
|
/// asynchronously and the method returns a Task that can be awaited to obtain the result.
|
/// </summary>
|
/// <typeparam name="T">Type</typeparam>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
|
/// <exception cref="Exception">Thrown if a Javascript error occurs.</exception>
|
/// <param name="frame">The 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>
|
/// <returns>
|
/// <see cref="Task{T}"/> that can be awaited to obtain the result of the script execution. The <see cref="ModelBinding.DefaultBinder"/>
|
/// is used to convert the result to the desired type. Property names are converted from camelCase.
|
/// If the script execution returns an error then an exception is thrown.
|
/// </returns>
|
public static async Task<T> EvaluateScriptAsync<T>(this IFrame frame, string script, TimeSpan? timeout = null)
|
{
|
WebBrowserExtensions.ThrowExceptionIfFrameNull(frame);
|
|
if (timeout.HasValue && timeout.Value.TotalMilliseconds > uint.MaxValue)
|
{
|
throw new ArgumentOutOfRangeException("timeout", "Timeout greater than Maximum allowable value of " + UInt32.MaxValue);
|
}
|
|
var response = await frame.EvaluateScriptAsync(script, timeout: timeout, useImmediatelyInvokedFuncExpression: false).ConfigureAwait(false);
|
|
if (response.Success)
|
{
|
var binder = DefaultBinder.Instance;
|
|
return (T)binder.Bind(response.Result, typeof(T));
|
}
|
|
throw new Exception(response.Message);
|
}
|
|
/// <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>
|
/// <typeparam name="T">Type</typeparam>
|
/// <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{T}"/> that can be awaited to obtain the result of the JavaScript execution.
|
/// </returns>
|
public static Task<T> EvaluateScriptAsync<T>(this IBrowser browser, string script, TimeSpan? timeout = null)
|
{
|
WebBrowserExtensions.ThrowExceptionIfBrowserNull(browser);
|
|
using (var frame = browser.MainFrame)
|
{
|
return frame.EvaluateScriptAsync<T>(script, timeout: timeout);
|
}
|
}
|
|
/// <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>
|
/// <typeparam name="T">Type</typeparam>
|
/// <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{T}"/> that can be awaited to obtain the result of the script execution.
|
/// </returns>
|
public static Task<T> EvaluateScriptAsync<T>(this IChromiumWebBrowserBase chromiumWebBrowser, string script, TimeSpan? timeout = null)
|
{
|
WebBrowserExtensions.ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser);
|
|
if (chromiumWebBrowser is IWebBrowser b)
|
{
|
if (b.CanExecuteJavascriptInMainFrame == false)
|
{
|
WebBrowserExtensions.ThrowExceptionIfCanExecuteJavascriptInMainFrameFalse();
|
}
|
}
|
|
return chromiumWebBrowser.BrowserCore.EvaluateScriptAsync<T>(script, timeout);
|
}
|
}
|
}
|