// 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); } } }