Пример #1
0
        /// <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);
        }
Пример #2
0
        /// <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));
        }
Пример #3
0
        /// <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);
        }
Пример #4
0
        /// <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));
        }
Пример #5
0
        /// <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);
            }
        }
Пример #6
0
        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;
        }
Пример #7
0
        /// <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));
        }
Пример #8
0
        /// <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));
        }
Пример #9
0
        /// <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());
        }
Пример #10
0
        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;
        }