// 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
{
///
/// Extended WebBrowserExtensions
///
public static class WebBrowserExtensionsEx
{
///
/// Retrieve the current . Contains information like
/// and
///
/// The ChromiumWebBrowser instance this method extends.
///
/// that when executed returns the current or null
///
public static Task GetVisibleNavigationEntryAsync(this IChromiumWebBrowserBase browser)
{
var host = browser.GetBrowserHost();
if (host == null)
{
return Task.FromResult(null);
}
if(Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
var entry = host.GetVisibleNavigationEntry();
return Task.FromResult(entry);
}
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
Cef.UIThreadTaskFactory.StartNew(delegate
{
var entry = host.GetVisibleNavigationEntry();
tcs.TrySetResult(entry);
});
return tcs.Task;
}
///
/// Downloads the specified and calls
/// when the download is complete. Makes a GET Request.
///
/// valid frame
/// url to download
/// Action to be executed when the download is complete.
public static void DownloadUrl(this IFrame frame, string url, Action 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);
});
}
///
/// Downloads the specified as a .
/// Makes a GET Request.
///
/// valid frame
/// url to download
/// control caching policy
/// A task that can be awaited to get the representing the Url
public static Task 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(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;
}
///
/// Toggles audio mute for the current browser.
/// If the is null or has been disposed
/// then this command will be a no-op.
///
/// The ChromiumWebBrowser instance this method extends.
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);
});
}
///
/// Evaluate javascript code in the context of the . The script will be executed
/// asynchronously and the method returns a Task that can be awaited to obtain the result.
///
/// Type
/// Thrown when one or more arguments are outside the required range.
/// Thrown if a Javascript error occurs.
/// The IFrame instance this method extends.
/// The Javascript code that should be executed.
/// (Optional) The timeout after which the Javascript code execution should be aborted.
///
/// that can be awaited to obtain the result of the script execution. The
/// 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.
///
public static async Task EvaluateScriptAsync(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);
}
///
/// 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
///
/// Type
/// Thrown when one or more arguments are outside the required range.
/// The IBrowser instance this method extends.
/// The JavaScript code that should be executed.
/// (Optional) The timeout after which the JavaScript code execution should be aborted.
///
/// that can be awaited to obtain the result of the JavaScript execution.
///
public static Task EvaluateScriptAsync(this IBrowser browser, string script, TimeSpan? timeout = null)
{
WebBrowserExtensions.ThrowExceptionIfBrowserNull(browser);
using (var frame = browser.MainFrame)
{
return frame.EvaluateScriptAsync(script, timeout: timeout);
}
}
///
/// 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
///
/// Thrown when one or more arguments are outside the required range.
/// Type
/// The ChromiumWebBrowser instance this method extends.
/// The Javascript code that should be executed.
/// (Optional) The timeout after which the Javascript code execution should be aborted.
///
/// that can be awaited to obtain the result of the script execution.
///
public static Task EvaluateScriptAsync(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(script, timeout);
}
}
}