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