/// <summary> /// Open DevTools using your own Control as the parent. If inspectElementAtX and/or inspectElementAtY are specified then /// the element at the specified (x,y) location will be inspected. /// For resize/moving to work correctly you will need to use the <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/> implementation. /// (Set <see cref="ChromiumWebBrowser.LifeSpanHandler"/> to an instance of <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/>) /// </summary> /// <param name="chromiumWebBrowser"><see cref="ChromiumWebBrowser"/> instance</param> /// <param name="addParentControl"> /// Action that is Invoked when the DevTools Host Control has been created and needs to be added to it's parent. /// It's important the control is added to it's intended parent at this point so the <see cref="Control.ClientRectangle"/> /// can be calculated to set the initial display size.</param> /// <param name="inspectElementAtX">x coordinate (used for inspectElement)</param> /// <param name="inspectElementAtY">y coordinate (used for inspectElement)</param> /// <returns>Returns the <see cref="Control"/> that hosts the DevTools instance if successful, otherwise returns null on error.</returns> public static Control ShowDevToolsDocked(this IChromiumWebBrowserBase chromiumWebBrowser, Action <ChromiumHostControl> addParentControl, string controlName = nameof(ChromiumHostControl) + "DevTools", DockStyle dockStyle = DockStyle.Fill, int inspectElementAtX = 0, int inspectElementAtY = 0) { if (chromiumWebBrowser.IsDisposed || addParentControl == null) { return(null); } var host = chromiumWebBrowser.GetBrowserHost(); if (host == null) { return(null); } var control = new ChromiumHostControl() { Name = controlName, Dock = dockStyle }; control.CreateControl(); //It's now time for the user to add the control to it's parent addParentControl(control); //Devtools will be a child of the ChromiumHostControl var rect = control.ClientRectangle; var windowInfo = new WindowInfo(); var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height); windowInfo.SetAsChild(control.Handle, windowBounds); host.ShowDevTools(windowInfo, inspectElementAtX, inspectElementAtY); return(control); }
/// <summary> /// Manually call https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow /// passing in the handle returned from <see cref="IBrowserHost.GetWindowHandle"/>. /// This method can be used to manually close the underlying CefBrowser instance. /// This will avoid the WM_Close message that CEF sends by default to the top level window. /// (Which closes your application). This method should generally only be used in the WinForms version. /// </summary> /// <param name="chromiumWebBrowser">the <see cref="ChromiumWebBrowser"/> or <see cref="ChromiumHostControl"/> instance.</param> /// <returns>If the function succeeds, the return value is true.</returns> /// <example> /// <code> /// //Invoke on the CEF UI Thread /// Cef.UIThreadTaskFactory.StartNew(() => /// { /// var closed = chromiumWebBrowser.DestroyWindow(); /// }); /// </code> /// </example> public static bool DestroyWindow(this IChromiumWebBrowserBase chromiumWebBrowser) { if (!Cef.CurrentlyOnThread(CefThreadIds.TID_UI)) { throw new InvalidOperationException("This method can only be called on the CEF UI thread." + "Use Cef.UIThreadTaskFactory to marshal your call onto the CEF UI Thread."); } if (chromiumWebBrowser.IsDisposed) { return(false); } var browser = chromiumWebBrowser.BrowserCore; if (browser == null) { return(false); } var handle = browser.GetHost().GetWindowHandle(); return(DestroyWindow(handle)); }
/// <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>(); Cef.UIThreadTaskFactory.StartNew(delegate { var entry = host.GetVisibleNavigationEntry(); tcs.TrySetResultAsync(entry); }); return(tcs.Task); }
/// <summary> /// Open DevTools using <paramref name="parentControl"/> as the parent control. If inspectElementAtX and/or inspectElementAtY are specified then /// the element at the specified (x,y) location will be inspected. /// For resize/moving to work correctly you will need to use the <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/> implementation. /// (Set <see cref="ChromiumWebBrowser.LifeSpanHandler"/> to an instance of <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/>) /// </summary> /// <param name="chromiumWebBrowser"><see cref="ChromiumWebBrowser"/> instance</param> /// <param name="parentControl">Control used as the parent for DevTools (a custom control will be added to the <see cref="Control.Controls"/> collection)</param> /// <param name="inspectElementAtX">x coordinate (used for inspectElement)</param> /// <param name="inspectElementAtY">y coordinate (used for inspectElement)</param> /// <returns>Returns the <see cref="Control"/> that hosts the DevTools instance if successful, otherwise returns null on error.</returns> public static Control ShowDevToolsDocked(this IChromiumWebBrowserBase chromiumWebBrowser, Control parentControl, string controlName = nameof(ChromiumHostControl) + "DevTools", DockStyle dockStyle = DockStyle.Fill, int inspectElementAtX = 0, int inspectElementAtY = 0) { if (chromiumWebBrowser.IsDisposed || parentControl == null || parentControl.IsDisposed) { return(null); } return(chromiumWebBrowser.ShowDevToolsDocked((ctrl) => { parentControl.Controls.Add(ctrl); }, controlName, dockStyle, inspectElementAtX, inspectElementAtY)); }
/// <summary> /// Calls Page.captureScreenshot without any optional params /// (Results in PNG image of default viewport) /// https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot /// </summary> /// <param name="browser">the ChromiumWebBrowser</param> /// <returns>png encoded image as byte[]</returns> public static async Task <byte[]> CaptureScreenShotAsPng(this IChromiumWebBrowserBase chromiumWebBrowser) { //Make sure to dispose of our observer registration when done //If you need to make multiple calls then reuse the devtools client //and Dispose when done. using (var devToolsClient = chromiumWebBrowser.GetDevToolsClient()) { var result = await devToolsClient.Page.CaptureScreenshotAsync(); return(result.Data); } }
public BrowserTabUserControl(ChromiumHostControl chromiumHostControl) { InitializeComponent(); Browser = chromiumHostControl; browserPanel.Controls.Add(chromiumHostControl); chromiumHostControl.LoadingStateChanged += OnBrowserLoadingStateChanged; chromiumHostControl.ConsoleMessage += OnBrowserConsoleMessage; chromiumHostControl.TitleChanged += OnBrowserTitleChanged; chromiumHostControl.AddressChanged += OnBrowserAddressChanged; chromiumHostControl.StatusMessage += OnBrowserStatusMessage; chromiumHostControl.IsBrowserInitializedChanged += OnIsBrowserInitializedChanged; chromiumHostControl.LoadError += OnLoadError; }
/// <summary> /// Execute a method call over the DevTools protocol. This is a more structured /// version of SendDevToolsMessage. <see cref="ExecuteDevToolsMethod"/> can only be called on the /// CEF UI Thread, this method can be called on any thread. /// See the DevTools protocol documentation at https://chromedevtools.github.io/devtools-protocol/ for details /// of supported methods and the expected <paramref name="parameters"/> dictionary contents. /// See the SendDevToolsMessage documentation for additional usage information. /// </summary> /// <param name="chromiumWebBrowser">the ChromiumWebBrowser instance</param> /// <param name="messageId">is an incremental number that uniquely identifies the message (pass 0 to have the next number assigned /// automatically based on previous values)</param> /// <param name="method">is the method name</param> /// <param name="parameters">are the method parameters represented as a dictionary, /// which may be empty.</param> /// <returns>return a Task that can be awaited to obtain the assigned message Id. If the message was /// unsuccessfully submitted for validation, this value will be 0.</returns> public static Task <int> ExecuteDevToolsMethodAsync(this IChromiumWebBrowserBase chromiumWebBrowser, int messageId, string method, IDictionary <string, object> parameters = null) { var browser = chromiumWebBrowser.BrowserCore; return(browser.ExecuteDevToolsMethodAsync(messageId, method, parameters)); }
/// <summary> /// Set the Document Content for the Main Frame using DevTools Protocol. /// </summary> /// <param name="chromiumWebBrowser">ChromiumWebBrowser instance</param> /// <param name="html">html</param> /// <returns>Task that can be awaited to determine if the content was successfully updated.</returns> public static Task <bool> SetMainFrameDocumentContentAsync(this IChromiumWebBrowserBase chromiumWebBrowser, string html) { var browser = chromiumWebBrowser.BrowserCore; return(browser.SetMainFrameDocumentContentAsync(html)); }
/// <summary> /// Gets a new Instance of the DevTools client for the chromiumWebBrowser /// instance. /// </summary> /// <param name="chromiumWebBrowser">the chromiumWebBrowser instance</param> /// <returns>DevToolsClient</returns> public static DevToolsClient GetDevToolsClient(this IChromiumWebBrowserBase chromiumWebBrowser) { var browser = chromiumWebBrowser.BrowserCore; return(browser.GetDevToolsClient()); }
public BrowserTabUserControl(Action <string, int?> openNewTab, string url, bool multiThreadedMessageLoopEnabled) { InitializeComponent(); var browser = new ChromiumWebBrowser(url) { Dock = DockStyle.Fill }; browserPanel.Controls.Add(browser); Browser = browser; browser.MenuHandler = new MenuHandler(); browser.RequestHandler = new WinFormsRequestHandler(openNewTab); browser.JsDialogHandler = new JsDialogHandler(); browser.DownloadHandler = new DownloadHandler(); browser.AudioHandler = new CefSharp.Handler.AudioHandler(); browser.FrameHandler = new CefSharp.Handler.FrameHandler(); if (multiThreadedMessageLoopEnabled) { browser.KeyboardHandler = new KeyboardHandler(); } //The CefSharp.WinForms.Handler.LifeSpanHandler implementation //allows for Popups to be hosted in Controls/Tabs //This example also demonstrates docking DevTools in a SplitPanel browser.LifeSpanHandler = CefSharp.WinForms.Handler.LifeSpanHandler .Create() .OnBeforePopupCreated((chromiumWebBrowser, b, frame, targetUrl, targetFrameName, targetDisposition, userGesture, browserSettings) => { //Can cancel opening popup based on Url if required. if (targetUrl?.StartsWith(CefExample.BaseUrl + "/cancelme.html") == true) { return(PopupCreation.Cancel); } return(PopupCreation.Continue); }) .OnPopupCreated((ctrl, targetUrl) => { //Don't try using ctrl.FindForm() here as //the control hasn't been attached to a parent yet. if (FindForm() is BrowserForm owner) { owner.AddTab(ctrl, targetUrl); } }) .OnPopupDestroyed((ctrl, popupBrowser) => { //If we docked DevTools (hosted it ourselves rather than the default popup) //Used when the BrowserTabUserControl.ShowDevToolsDocked method is called if (popupBrowser.MainFrame.Url.Equals("devtools://devtools/devtools_app.html")) { //Dispose of the parent control we used to host DevTools, this will release the DevTools window handle //and the ILifeSpanHandler.OnBeforeClose() will be call after. ctrl.Dispose(); } else { //If browser is disposed or the handle has been released then we don't //need to remove the tab in this example. The user likely used the // File -> Close Tab menu option which also calls BrowserForm.RemoveTab if (!ctrl.IsDisposed && ctrl.IsHandleCreated) { if (ctrl.FindForm() is BrowserForm owner) { var windowHandle = popupBrowser.GetHost().GetWindowHandle(); owner.RemoveTab(windowHandle); } ctrl.Dispose(); } } }) .OnPopupBrowserCreated((ctrl, popupBrowser) => { //The host control maybe null if the popup was hosted in a native Window e.g. Devtools by default if (ctrl == null) { return; } //You can access all the core browser functionality via IBrowser //frames, browwser host, etc. var isPopup = popupBrowser.IsPopup; }) .Build(); browser.LoadingStateChanged += OnBrowserLoadingStateChanged; browser.ConsoleMessage += OnBrowserConsoleMessage; browser.TitleChanged += OnBrowserTitleChanged; browser.AddressChanged += OnBrowserAddressChanged; browser.StatusMessage += OnBrowserStatusMessage; browser.IsBrowserInitializedChanged += OnIsBrowserInitializedChanged; browser.LoadError += OnLoadError; #if NETCOREAPP browser.JavascriptObjectRepository.Register("boundAsync", new AsyncBoundObject(), options: BindingOptions.DefaultBinder); #else browser.JavascriptObjectRepository.Register("bound", new BoundObject(), isAsync: false, options: BindingOptions.DefaultBinder); browser.JavascriptObjectRepository.Register("boundAsync", new AsyncBoundObject(), isAsync: true, options: BindingOptions.DefaultBinder); #endif //If you call CefSharp.BindObjectAsync in javascript and pass in the name of an object which is not yet //bound, then ResolveObject will be called, you can then register it browser.JavascriptObjectRepository.ResolveObject += (sender, e) => { var repo = e.ObjectRepository; if (e.ObjectName == "boundAsync2") { #if NETCOREAPP repo.Register("boundAsync2", new AsyncBoundObject(), options: BindingOptions.DefaultBinder); #else repo.Register("boundAsync2", new AsyncBoundObject(), isAsync: true, options: BindingOptions.DefaultBinder); #endif } }; browser.RenderProcessMessageHandler = new RenderProcessMessageHandler(); browser.DisplayHandler = new WinFormsDisplayHandler(); //browser.MouseDown += OnBrowserMouseClick; this.multiThreadedMessageLoopEnabled = multiThreadedMessageLoopEnabled; var eventObject = new ScriptedMethodsBoundObject(); eventObject.EventArrived += OnJavascriptEventArrived; // Use the default of camelCaseJavascriptNames // .Net methods starting with a capitol will be translated to starting with a lower case letter when called from js #if !NETCOREAPP browser.JavascriptObjectRepository.Register("boundEvent", eventObject, isAsync: false, options: BindingOptions.DefaultBinder); #endif CefExample.RegisterTestResources(browser); var version = string.Format("Chromium: {0}, CEF: {1}, CefSharp: {2}", Cef.ChromiumVersion, Cef.CefVersion, Cef.CefSharpVersion); //Set label directly, don't use DisplayOutput as call would be a NOOP (no valid handle yet). outputLabel.Text = version; }