// Copyright © 2014 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. namespace CefSharp.WinForms.Internals { /// /// Default implementation of /// for the WinForms implementation /// /// public class DefaultFocusHandler : IFocusHandler { /// /// Called when the browser component has received focus. /// /// the ChromiumWebBrowser control /// the browser object /// Try to avoid needing to override this logic in a subclass. The implementation in /// DefaultFocusHandler relies on very detailed behavior of how WinForms and /// Windows interact during window activation. public virtual void OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser) { //We don't deal with popups as they're rendered by default entirely by CEF //For print dialogs the browser will be null, we don't want to deal with that either. if (browser == null || browser.IsPopup) { return; } var winFormsChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser; // During application activation, CEF receives a WM_SETFOCUS // message from Windows because it is the top window // on the CEF UI thread. // // If the WinForm ChromiumWebBrowser control is the // current .ActiveControl before app activation // then we MUST NOT try to reactivate the WinForm // control during activation because that will // start a race condition between reactivating // the CEF control AND having another control // that should be the new .ActiveControl. // // For example: // * CEF control has focus, and thus ChromiumWebBrowser // is the current .ActiveControl // * Alt-Tab to another application // * Click a non CEF control in the WinForms application. // * This begins the Windows activation process. // * The WM_ACTIVATE process on the WinForm UI thread // will update .ActiveControl to the clicked control. // The clicked control will receive WM_SETFOCUS as well. // (i.e. OnGotFocus) // If the ChromiumWebBrowser was the previous .ActiveControl, // then we set .Activating = true. // * The WM_ACTIVATE process on the CEF thread will // send WM_SETFOCUS to CEF thus staring the race of // which will end first, the WndProc WM_ACTIVATE process // on the WinForm UI thread or the WM_ACTIVATE process // on the CEF UI thread. // * CEF will then call this method on the CEF UI thread // due to WM_SETFOCUS. // * This method will clear the activation state (if any) // on the ChromiumWebBrowser control, due to the race // condition the WinForm UI thread cannot. if (winFormsChromiumWebBrowser.IsActivating) { winFormsChromiumWebBrowser.IsActivating = false; } else { // Otherwise, we're not being activated // so we must activate the ChromiumWebBrowser control // for WinForms focus tracking. winFormsChromiumWebBrowser.InvokeOnUiThreadIfRequired(() => { winFormsChromiumWebBrowser.Activate(); }); } } /// /// Called when the browser component is requesting focus. /// /// the ChromiumWebBrowser control /// the browser object /// Indicates where the focus request is originating from. /// Return false to allow the focus to be set or true to cancel setting the focus. public virtual bool OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source) { //We don't deal with popups as they're rendered by default entirely by CEF if (browser.IsPopup) { return false; } // Do not let the browser take focus when a Load method has been called return source == CefFocusSource.FocusSourceNavigation; } /// /// Called when the browser component is about to lose focus. /// For instance, if focus was on the last HTML element and the user pressed the TAB key. /// /// the ChromiumWebBrowser control /// the browser object /// Will be true if the browser is giving focus to the next component /// and false if the browser is giving focus to the previous component. public virtual void OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next) { //We don't deal with popups as they're rendered by default entirely by CEF if (browser.IsPopup) { return; } var winFormsChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser; // NOTE: OnTakeFocus means leaving focus / not taking focus winFormsChromiumWebBrowser.InvokeOnUiThreadIfRequired(() => winFormsChromiumWebBrowser.SelectNextControl(next)); } } }