// 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 { /// /// WebBrowser extensions - These methods make performing common tasks easier. /// 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 /// /// Registers a Javascript object in this specific browser instance. /// /// The browser to perform the registering on. /// The name of the object. (e.g. "foo", if you want the object to be accessible as window.foo). /// The object to be made accessible to Javascript. /// (Optional) binding options - camelCaseJavascriptNames default to true. /// Browser is already initialized. RegisterJsObject must be + /// called before the underlying CEF browser is created. [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."); } /// /// Asynchronously registers a Javascript object in this specific browser instance. /// Only methods of the object will be availabe. /// /// The browser to perform the registering on /// The name of the object. (e.g. "foo", if you want the object to be accessible as window.foo). /// The object to be made accessible to Javascript. /// binding options - camelCaseJavascriptNames default to true /// Browser is already initialized. RegisterJsObject must be + /// called before the underlying CEF browser is created. /// 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. [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 /// /// Returns the main (top-level) frame for the browser window. /// /// the ChromiumWebBrowser instance. /// the main frame. public static IFrame GetMainFrame(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); var cefBrowser = browser.BrowserCore; ThrowExceptionIfBrowserNull(cefBrowser); return cefBrowser.MainFrame; } /// /// Returns the focused frame for the browser window. /// /// the ChromiumWebBrowser instance. /// the focused frame. public static IFrame GetFocusedFrame(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); var cefBrowser = browser.BrowserCore; ThrowExceptionIfBrowserNull(cefBrowser); return cefBrowser.FocusedFrame; } /// /// Execute Undo on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Undo(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Undo(); } /// /// Execute Undo on the focused frame. /// /// The IBrowser instance this method extends. public static void Undo(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Undo(); } } /// /// Execute Redo on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Redo(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Redo(); } /// /// Execute Redo on the focused frame. /// /// The IBrowser instance this method extends. public static void Redo(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Redo(); } } /// /// Execute Cut on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Cut(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Cut(); } /// /// Execute Cut on the focused frame. /// /// The IBrowser instance this method extends. public static void Cut(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Cut(); } } /// /// Execute Copy on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Copy(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Copy(); } /// /// Execute Copy on the focused frame. /// /// The IBrowser instance this method extends. public static void Copy(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Copy(); } } /// /// Execute Paste on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Paste(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Paste(); } /// /// Execute Paste on the focused frame. /// /// The IBrowser instance this method extends. public static void Paste(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Paste(); } } /// /// Execute Delete on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void Delete(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Delete(); } /// /// Execute Delete on the focused frame. /// /// The IBrowser instance this method extends. public static void Delete(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.Delete(); } } /// /// Execute SelectAll on the focused frame. /// /// The ChromiumWebBrowser instance this method extends. public static void SelectAll(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.SelectAll(); } /// /// Execute SelectAll on the focused frame. /// /// The IBrowser instance this method extends. public static void SelectAll(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); frame.SelectAll(); } } /// /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web page is /// shown. /// /// The ChromiumWebBrowser instance this method extends. public static void ViewSource(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.ViewSource(); } /// /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web page is /// shown. /// /// The IBrowser instance this method extends. public static void ViewSource(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.MainFrame) { ThrowExceptionIfFrameNull(frame); frame.ViewSource(); } } /// /// Retrieve the main frame's HTML source using a . /// /// The ChromiumWebBrowser instance this method extends. /// /// that when executed returns the main frame source as a string. /// public static Task GetSourceAsync(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); return browser.BrowserCore.GetSourceAsync(); } /// /// Retrieve the main frame's HTML source using a . /// /// The IBrowser instance this method extends. /// /// that when executed returns the main frame source as a string. /// public static Task GetSourceAsync(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); return frame.GetSourceAsync(); } } /// /// Retrieve the main frame's display text using a . /// /// The ChromiumWebBrowser instance this method extends. /// /// that when executed returns the main frame display text as a string. /// public static Task GetTextAsync(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); return browser.BrowserCore.GetTextAsync(); } /// /// Retrieve the main frame's display text using a . /// /// The IBrowser instance this method extends. /// /// that when executed returns the main frame display text as a string. /// public static Task GetTextAsync(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.FocusedFrame) { ThrowExceptionIfFrameNull(frame); return frame.GetTextAsync(); } } /// /// Download the file at url using . /// /// The ChromiumWebBrowser instance this method extends. /// url to download public static void StartDownload(this IChromiumWebBrowserBase browser, string url) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.StartDownload(url); } /// /// Download the file at url using . /// /// The IBrowser instance this method extends. /// url to download public static void StartDownload(this IBrowser browser, string url) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.StartDownload(url); } /// /// See for details /// /// ChromiumWebBrowser instance (cannot be null) /// /// Load the in the main frame of the browser /// /// url to load /// See for details public static Task LoadUrlAsync(IChromiumWebBrowserBase chromiumWebBrowser, string url) { ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser); if (string.IsNullOrEmpty(url)) { throw new ArgumentNullException(nameof(url)); } var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); EventHandler loadErrorHandler = null; EventHandler 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; } /// /// 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. /// /// /// Usage of the History API to change the URL is considered a navigation /// /// ChromiumWebBrowser instance (cannot be null) /// optional timeout, if not specified defaults to five(5) seconds. /// optional CancellationToken /// Task which resolves when has been called with false. /// or when is called to signify a load failure. /// /// /// /// /// /// public static async Task WaitForNavigationAsync(IChromiumWebBrowserBase chromiumWebBrowser, TimeSpan? timeout = null, CancellationToken cancellationToken = default) { ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); EventHandler loadErrorHandler = null; EventHandler 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; } } /// /// Waits for a DOM element specified by the 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. /// /// ChromiumWebBrowser instance (cannot be null) /// querySelector for the element e.g. #idOfMyElement /// timeout /// /// (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. /// /// A Task that resolves when element specified by selector string is added to or removed from the DOM. /// /// /// /// /// /// /// 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 if a navigation /// occurs whilst waiting to resolve. /// public static async Task 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)timeoutResponse.Result; var id = element["id"].ToString(); var tagName = element["tagName"].ToString(); return new WaitForSelectorAsyncResponse(id, tagName, true); } return new WaitForSelectorAsyncResponse(false, timeoutResponse.Message); } /// /// 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) /// /// The ChromiumWebBrowser instance this method extends. /// The javascript method name to execute. /// the arguments to be passed as params to the method. Args are encoded using /// , you can provide a custom implementation if you require one. public static void ExecuteScriptAsync(this IChromiumWebBrowserBase browser, string methodName, params object[] args) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.ExecuteScriptAsync(methodName, args); } /// /// 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) /// /// The IBrowser instance this method extends. /// The javascript method name to execute. /// the arguments to be passed as params to the method. Args are encoded using /// , you can provide a custom implementation if you require one. public static void ExecuteScriptAsync(this IBrowser browser, string methodName, params object[] args) { ThrowExceptionIfBrowserNull(browser); var script = GetScriptForJavascriptMethodWithArgs(methodName, args); browser.ExecuteScriptAsync(script); } /// /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// The Javascript code that should be executed. public static void ExecuteScriptAsync(this IChromiumWebBrowserBase browser, string script) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); using (var frame = browser.GetMainFrame()) { ThrowExceptionIfFrameNull(frame); frame.ExecuteJavaScriptAsync(script); } } /// /// 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. /// /// The IBrowser instance this method extends. /// The Javascript code that should be executed. public static void ExecuteScriptAsync(this IBrowser browser, string script) { ThrowExceptionIfBrowserNull(browser); using (var frame = browser.MainFrame) { ThrowExceptionIfFrameNull(frame); frame.ExecuteJavaScriptAsync(script); } } /// /// 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. /// /// /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// The Javascript code that should be executed. /// (Optional) The script will only be executed on first page load, subsequent page loads will be ignored. 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 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; } } /// /// Creates a new instance of IRequest with the specified Url and Method = POST and then calls /// . /// /// browser this method extends /// url to load /// post data as byte array /// (Optional) if set the Content-Type header will be set public static void LoadUrlWithPostData(this IChromiumWebBrowserBase browser, string url, byte[] postDataBytes, string contentType = null) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.LoadUrlWithPostData(url, postDataBytes, contentType); } /// /// Creates a new instance of IRequest with the specified Url and Method = POST and then calls /// . /// /// browser this method extends /// url to load /// post data as byte array /// (Optional) if set the Content-Type header will be set 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); } } /// /// Registers and loads a that represents the HTML content. /// /// /// `Cef` Native `LoadHtml` is unpredictable and only works sometimes, this method wraps the provided HTML in a /// and loads the provided url using the method. Defaults to using /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// The HTML content. /// The URL that will be treated as the address of the content. /// /// returns false if the Url was not successfully parsed into a Uri. /// public static bool LoadHtml(this IWebBrowser browser, string html, string url) { return browser.LoadHtml(html, url, Encoding.UTF8); } /// /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// Html to load as data uri. /// (Optional) if true the html string will be base64 encoded using UTF8 encoding. public static void LoadHtml(this IChromiumWebBrowserBase browser, string html, bool base64Encode = false) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); var htmlString = new HtmlString(html, base64Encode); browser.LoadUrl(htmlString.ToDataUriString()); } /// /// 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. /// /// The instance this method extends. /// Html to load as data uri. /// (Optional) if true the html string will be base64 encoded using UTF8 encoding. public static void LoadHtml(this IFrame frame, string html, bool base64Encode = false) { var htmlString = new HtmlString(html, base64Encode); frame.LoadUrl(htmlString.ToDataUriString()); } /// /// Registers and loads a that represents the HTML content. /// /// /// `Cef` Native `LoadHtml` is unpredictable and only works sometimes, this method wraps the provided HTML in a /// and loads the provided url using the method. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. /// The HTML content. /// The URL that will be treated as the address of the content. /// Character Encoding. /// (Optional) Whether or not the handler should be used once (true) or until manually unregistered /// (false) /// /// returns false if the Url was not successfully parsed into a Uri. /// 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; } /// /// Register a ResourceHandler. Can only be used when browser.ResourceHandlerFactory is an instance of /// DefaultResourceHandlerFactory. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. /// the url of the resource to unregister. /// Stream to be registered, the stream should not be shared with any other instances of /// DefaultResourceHandlerFactory. /// (Optional) the mimeType. /// (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. 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); } } /// /// Unregister a ResourceHandler. Can only be used when browser.ResourceHandlerFactory is an instance of /// DefaultResourceHandlerFactory. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. /// the url of the resource to unregister. 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); } /// /// Stops loading the current page. /// /// The ChromiumWebBrowser instance this method extends. public static void Stop(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Stop(); } /// /// Stops loading the current page. /// /// The IBrowser instance this method extends. public static void Stop(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); browser.StopLoad(); } /// /// Navigates back, must check before calling this method. /// /// The ChromiumWebBrowser instance this method extends. public static void Back(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Back(); } /// /// Navigates back, must check before calling this method. /// /// The IBrowser instance this method extends. public static void Back(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); browser.GoBack(); } /// /// Navigates forward, must check before calling this method. /// /// The ChromiumWebBrowser instance this method extends. public static void Forward(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Forward(); } /// /// Navigates forward, must check before calling this method. /// /// The IBrowser instance this method extends. public static void Forward(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); browser.GoForward(); } /// /// Reloads the page being displayed. This method will use data from the browser's cache, if available. /// /// The ChromiumWebBrowser instance this method extends. public static void Reload(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.Reload(false); } /// /// Reloads the page being displayed, optionally ignoring the cache (which means the whole page including all .css, .js etc. /// resources will be re-fetched). /// /// The ChromiumWebBrowser instance this method extends. /// true A reload is performed ignoring browser cache; false A reload is performed using /// files from the browser cache, if available. public static void Reload(this IChromiumWebBrowserBase browser, bool ignoreCache) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Reload(ignoreCache); } /// /// Reloads the page being displayed, optionally ignoring the cache (which means the whole page including all .css, .js etc. /// resources will be re-fetched). /// /// The IBrowser instance this method extends. /// true A reload is performed ignoring browser cache; false A reload is performed using /// files from the browser cache, if available. public static void Reload(this IBrowser browser, bool ignoreCache = false) { ThrowExceptionIfBrowserNull(browser); browser.Reload(ignoreCache); } /// /// Gets the default cookie manager associated with the instance. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. /// (Optional) If not null it will be executed asynchronously on the CEF IO thread after the manager's /// storage has been initialized. /// /// Cookie Manager. /// 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); } /// /// Gets the RequestContext associated with the instance. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. /// /// RequestContext /// public static IRequestContext GetRequestContext(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); var host = browser.GetBrowserHost(); ThrowExceptionIfBrowserHostNull(host); return host.RequestContext; } /// /// Asynchronously gets the current Zoom Level. /// /// The ChromiumWebBrowser instance this method extends. /// /// An asynchronous result that yields the zoom level. /// public static Task GetZoomLevelAsync(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); return host.GetZoomLevelAsync(); } /// /// Asynchronously gets the current Zoom Level. /// /// the ChromiumWebBrowser instance. /// /// An asynchronous result that yields the zoom level. /// public static Task GetZoomLevelAsync(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); return browser.BrowserCore.GetZoomLevelAsync(); } /// /// Change the ZoomLevel to the specified value. Can be set to 0.0 to clear the zoom level. /// /// /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// zoom level. public static void SetZoomLevel(this IBrowser browser, double zoomLevel) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.SetZoomLevel(zoomLevel); } /// /// Change the ZoomLevel to the specified value. Can be set to 0.0 to clear the zoom level. /// /// /// 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. /// /// The ChromiumWebBrowser instance this method extends. /// zoom level. public static void SetZoomLevel(this IChromiumWebBrowserBase browser, double zoomLevel) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.SetZoomLevel(zoomLevel); } /// /// Search for text within the current page. /// /// The instance this method extends. /// text to search for /// indicates whether to search forward or backward within the page /// indicates whether the search should be case-sensitive /// indicates whether this is the first request or a follow-up /// The instance, if any, will be called to report find results. 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); } /// /// Search for text within the current page. /// /// The ChromiumWebBrowser instance this method extends. /// text to search for /// indicates whether to search forward or backward within the page /// indicates whether the search should be case-sensitive /// indicates whether this is the first request or a follow-up /// The instance, if any, will be called to report find results. public static void Find(this IChromiumWebBrowserBase browser, string searchText, bool forward, bool matchCase, bool findNext) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Find(searchText, forward, matchCase, findNext); } /// /// Cancel all searches that are currently going on. /// /// The instance this method extends. /// clear the current search selection. public static void StopFinding(this IBrowser browser, bool clearSelection) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.StopFinding(clearSelection); } /// /// Cancel all searches that are currently going on. /// /// The ChromiumWebBrowser instance this method extends. /// clear the current search selection. public static void StopFinding(this IChromiumWebBrowserBase browser, bool clearSelection) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.StopFinding(clearSelection); } /// /// Opens a Print Dialog which if used (can be user cancelled) will print the browser contents. /// /// The browser instance this method extends. public static void Print(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.Print(); } /// /// Opens a Print Dialog which if used (can be user cancelled) will print the browser contents. /// /// The ChromiumWebBrowser instance this method extends. public static void Print(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.Print(); } /// /// Asynchronously prints the current browser contents to the PDF file specified. The caller is responsible for deleting the file /// when done. /// /// The object this method extends. /// Output file location. /// (Optional) Print Settings. /// /// A task that represents the asynchronous print operation. The result is true on success or false on failure to generate the /// Pdf. /// public static Task 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; } /// /// Asynchronously prints the current browser contents to the PDF file specified. The caller is responsible for deleting the file /// when done. /// /// The ChromiumWebBrowser instance this method extends. /// Output file location. /// (Optional) Print Settings. /// /// A task that represents the asynchronous print operation. The result is true on success or false on failure to generate the /// Pdf. /// public static Task PrintToPdfAsync(this IChromiumWebBrowserBase browser, string path, PdfPrintSettings settings = null) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); return browser.BrowserCore.PrintToPdfAsync(path, settings); } /// /// Open developer tools in its own window. /// /// The instance this method extends. /// (Optional) window info used for showing dev tools. /// (Optional) x coordinate (used for inspectElement) /// (Optional) y coordinate (used for inspectElement) 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); } /// /// Open developer tools in its own window. /// /// The ChromiumWebBrowser instance this method extends. /// (Optional) window info used for showing dev tools. /// (Optional) x coordinate (used for inspectElement) /// (Optional) y coordinate (used for inspectElement) public static void ShowDevTools(this IChromiumWebBrowserBase browser, IWindowInfo windowInfo = null, int inspectElementAtX = 0, int inspectElementAtY = 0) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.ShowDevTools(windowInfo, inspectElementAtX, inspectElementAtY); } /// /// Explicitly close the developer tools window if one exists for this browser instance. /// /// The instance this method extends. public static void CloseDevTools(this IBrowser browser) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.CloseDevTools(); } /// /// Explicitly close the developer tools window if one exists for this browser instance. /// /// The ChromiumWebBrowser instance this method extends. public static void CloseDevTools(this IChromiumWebBrowserBase browser) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.CloseDevTools(); } /// /// If a misspelled word is currently selected in an editable node calling this method will replace it with the specified word. /// /// The instance this method extends. /// The new word that will replace the currently selected word. public static void ReplaceMisspelling(this IBrowser browser, string word) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.ReplaceMisspelling(word); } /// /// If a misspelled word is currently selected in an editable node calling this method will replace it with the specified word. /// /// The ChromiumWebBrowser instance this method extends. /// The new word that will replace the currently selected word. public static void ReplaceMisspelling(this IChromiumWebBrowserBase browser, string word) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.ReplaceMisspelling(word); } /// /// Add the specified word to the spelling dictionary. /// /// The instance this method extends. /// The new word that will be added to the dictionary. public static void AddWordToDictionary(this IBrowser browser, string word) { ThrowExceptionIfBrowserNull(browser); var host = browser.GetHost(); ThrowExceptionIfBrowserHostNull(host); host.AddWordToDictionary(word); } /// /// Add the specified word to the spelling dictionary. /// /// The ChromiumWebBrowser instance this method extends. /// The new word that will be added to the dictionary. public static void AddWordToDictionary(this IChromiumWebBrowserBase browser, string word) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); browser.BrowserCore.AddWordToDictionary(word); } /// /// Shortcut method to get the browser IBrowserHost. /// /// The ChromiumWebBrowser instance this method extends. /// /// browserHost or null. /// public static IBrowserHost GetBrowserHost(this IChromiumWebBrowserBase browser) { return browser.BrowserCore?.GetHost(); } /// /// Send a mouse wheel event to the browser. /// /// The ChromiumWebBrowser instance this method extends. /// The x coordinate relative to upper-left corner of view. /// The y coordinate relative to upper-left corner of view. /// The delta x coordinate. /// The delta y coordinate. /// The modifiers. 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); } /// /// Send a mouse wheel event to the browser. /// /// The instance this method extends. /// The x coordinate relative to upper-left corner of view. /// The y coordinate relative to upper-left corner of view. /// The delta x coordinate. /// The delta y coordinate. /// The modifiers. 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); } /// /// Send a mouse wheel event to the browser. /// /// browserHost. /// The x coordinate relative to upper-left corner of view. /// The y coordinate relative to upper-left corner of view. /// The delta x coordinate. /// The delta y coordinate. /// The modifiers. 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); } /// /// Send a mouse click event to the browser. /// /// browserHost. /// The x coordinate relative to upper-left corner of view. /// The y coordinate relative to upper-left corner of view. /// Type of the mouse button. /// True to mouse up. /// Number of clicks. /// The modifiers. 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); } /// /// Send a mouse move event to the browser. /// /// browserHost. /// The x coordinate relative to upper-left corner of view. /// The y coordinate relative to upper-left corner of view. /// mouse leave. /// The modifiers. 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); } /// /// 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. /// /// Thrown when one or more arguments are outside the required range. /// 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 perform the script execution. /// public static Task 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); } /// /// 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. /// /// 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 perform the script execution. /// public static Task EvaluateScriptAsPromiseAsync(this IBrowser browser, string script, TimeSpan? timeout = null) { var promiseHandlerScript = GetPromiseHandlerScript(script, null); return browser.EvaluateScriptAsync(promiseHandlerScript, timeout: timeout, useImmediatelyInvokedFuncExpression: true); } /// /// 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. /// /// Thrown when one or more arguments are outside the required range. /// The instance this method extends. /// The Javascript code that should be executed. /// (Optional) The timeout after which the Javascript code execution should be aborted. /// /// Only required if a custom value was specified for /// then this param must match that value. Otherwise exclude passing a value for this param or pass in null. /// /// /// that can be awaited to perform the script execution. /// public static Task 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; } /// /// 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. /// 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. /// 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; /// /// /// that can be awaited to obtain the result of the script execution. /// public static Task 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); } /// /// 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 /// /// 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. /// 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; /// /// /// that can be awaited to obtain the result of the script execution. /// public static Task 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); } } /// /// 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) /// /// The ChromiumWebBrowser instance this method extends. /// The javascript method name to execute. /// the arguments to be passed as params to the method. /// /// that can be awaited to obtain the result of the script execution. /// public static Task EvaluateScriptAsync(this IChromiumWebBrowserBase browser, string methodName, params object[] args) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); return browser.EvaluateScriptAsync(null, methodName, args); } /// /// 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). /// /// The ChromiumWebBrowser instance this method extends. /// The timeout after which the Javascript code execution should be aborted. /// The javascript method name to execute. /// the arguments to be passed as params to the method. Args are encoded using /// , you can provide a custom implementation if you require a custom implementation. /// /// that can be awaited to perform the script execution. /// public static Task EvaluateScriptAsync(this IChromiumWebBrowserBase browser, TimeSpan? timeout, string methodName, params object[] args) { ThrowExceptionIfChromiumWebBrowserDisposed(browser); var script = GetScriptForJavascriptMethodWithArgs(methodName, args); return browser.EvaluateScriptAsync(script, timeout); } /// /// An IWebBrowser extension method that sets the /// property used when passing a ChromiumWebBrowser instance to /// /// The ChromiumWebBrowser instance this method extends. public static void SetAsPopup(this IWebBrowser browser) { var internalBrowser = (IWebBrowserInternal)browser; internalBrowser.HasParent = true; } /// /// Dispose of the DevToolsContext (if any). Used in conjunction with CefSharp.Dom /// /// ChromiumWebBrowser instance public static void DisposeDevToolsContext(this IWebBrowserInternal webBrowserInternal) { if(webBrowserInternal == null) { return; } webBrowserInternal.DevToolsContext?.Dispose(); webBrowserInternal.DevToolsContext = null; } /// /// Set the property to null. Used in conjunction with CefSharp.Dom /// /// ChromiumWebBrowser instance public static void FreeDevToolsContext(this IWebBrowserInternal webBrowserInternal) { if (webBrowserInternal == null) { return; } webBrowserInternal.DevToolsContext = null; } /// /// Function used to encode the params passed to , /// and /// /// Provide your own custom function to perform custom encoding. You can use your choice of JSON encoder here if you should so /// choose. /// /// /// A function delegate that yields a string. /// public static Func EncodeScriptParam { get; set; } = (str) => { return str.Replace("\\", "\\\\") .Replace("'", "\\'") .Replace("\t", "\\t") .Replace("\r", "\\r") .Replace("\n", "\\n"); }; /// /// Checks if the given object is a numerical object. /// /// The object to check. /// /// True if numeric, otherwise false. /// 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; } /// /// Transforms the methodName and arguments into valid Javascript code. Will encapsulate params in single quotes (unless int, /// uint, etc) /// /// The javascript method name to execute. /// the arguments to be passed as params to the method. /// /// The Javascript code. /// 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); } } /// /// Throw exception if frame null. /// /// Thrown when an exception error condition occurs. /// The instance this method extends. public static void ThrowExceptionIfFrameNull(IFrame frame) { if (frame == null) { throw new Exception(FrameNullExceptionString); } } /// /// An IBrowser extension method that throw exception if browser null. /// /// Thrown when an exception error condition occurs. /// The ChromiumWebBrowser instance this method extends. public static void ThrowExceptionIfBrowserNull(IBrowser browser) { if (browser == null) { throw new Exception(BrowserNullExceptionString); } } /// /// Throw exception if browser host null. /// /// Thrown when an exception error condition occurs. /// The browser host. public static void ThrowExceptionIfBrowserHostNull(IBrowserHost browserHost) { if (browserHost == null) { throw new Exception(BrowserHostNullExceptionString); } } /// /// Throw exception if can execute javascript in main frame false. /// /// Thrown when an exception error condition occurs. 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)."); } } }