// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using Windows.Win32.System.Com;
using Windows.Win32.System.Ole;

namespace System.Windows.Forms;

/// <summary>
///  Wraps ActiveX controls and exposes them as fully featured windows forms controls
///  (by inheriting from Control). Some of Control's properties that don't make sense
///  for ActiveX controls are blocked here (by setting Browsable attributes on some and
///  throwing exceptions from others), to make life easy for the inheritors.
///
///  Inheritors of this class simply need to concentrate on defining and implementing the
///  properties/methods/events of the specific ActiveX control they are wrapping, the
///  default properties etc and the code to implement the activation etc. are
///  encapsulated in the class below.
///
///  The classid of the ActiveX control is specified in the constructor.
/// </summary>
[DefaultProperty(nameof(Name))]
[DefaultEvent(nameof(Enter))]
[Designer($"System.Windows.Forms.Design.AxDesigner, {Assemblies.SystemDesign}")]
public unsafe partial class WebBrowserBase : Control
{
    private WebBrowserHelper.AXState _axState = WebBrowserHelper.AXState.Passive;
    private WebBrowserHelper.AXState _axReloadingState = WebBrowserHelper.AXState.Passive;
    private WebBrowserHelper.AXEditMode _axEditMode = WebBrowserHelper.AXEditMode.None;
    private BitVector32 _axHostState;
    private WebBrowserHelper.SelectionStyle _selectionStyle = WebBrowserHelper.SelectionStyle.NotSelected;
    private WebBrowserSiteBase? _axSite;
    private ContainerControl? _containingControl;
    private EventHandler? _selectionChangeHandler;
    private readonly Guid _clsid;
    // Pointers to the ActiveX object: Interface pointers are cached for perf.
    private IOleObject.Interface? _axOleObject;
    private IOleInPlaceObject.Interface? _axOleInPlaceObject;
    private IOleInPlaceActiveObject.Interface? _axOleInPlaceActiveObject;
    private IOleControl.Interface? _axOleControl;
    private WebBrowserBaseNativeWindow? _axWindow;
    // We need to change the size of the inner ActiveX control before the
    // WebBrowserBase control's size is changed (i.e., before WebBrowserBase.Bounds
    // is changed) for better visual effect. We use this field to know what size
    // the WebBrowserBase control is changing to.
    private Size _webBrowserBaseChangingSize = Size.Empty;
    private WebBrowserContainer? _wbContainer;

    // This flags the WebBrowser not to process dialog keys when the ActiveX control is doing it
    // and calls back into the WebBrowser for some reason.
    private bool _ignoreDialogKeys;

    //
    // Internal fields:
    //
    internal WebBrowserContainer? _container;
    internal object? _activeXInstance;

    /// <summary>
    ///  Creates a new instance of a WinForms control which wraps an ActiveX control
    ///  given by the clsid parameter.
    /// </summary>
    internal WebBrowserBase(string clsidString) : base()
    {
        if (Application.OleRequired() != ApartmentState.STA)
        {
            throw new ThreadStateException(string.Format(SR.AXMTAThread, clsidString));
        }

        SetStyle(ControlStyles.UserPaint, false);

        _clsid = new Guid(clsidString);
        _webBrowserBaseChangingSize.Width = -1;  // Invalid value. Use WebBrowserBase.Bounds instead, when this is the case.
        SetAXHostState(WebBrowserHelper.s_isMaskEdit, _clsid.Equals(WebBrowserHelper.s_maskEdit_Clsid));
    }

    //
    // Public properties:
    //

    /// <summary>
    ///  Returns the native webbrowser object that this control wraps.
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public object? ActiveXInstance => _activeXInstance;

    //
    // Virtual methods:
    //
    // The following are virtual methods that derived-classes can override
    //

    //
    // The native ActiveX control QI's for interfaces on it's site to see if
    // it needs to change its behavior. Since the WebBrowserSiteBaseBase class is generic,
    // it only implements site interfaces that are generic to all sites. QI's
    // for any more specific interfaces will fail. This is a problem if anyone
    // wants to support any other interfaces on the site. In order to overcome
    // this, one needs to extend WebBrowserSiteBaseBase and implement any additional interfaces
    // needed.
    //
    // ActiveX wrapper controls that derive from this class should override the
    // below method and return their own WebBrowserSiteBaseBase derived object.
    //
    /// <summary>
    ///  Returns an object that will be set as the site for the native ActiveX control.
    ///  Implementors of the site can derive from <see cref="WebBrowserSiteBase"/> class.
    /// </summary>
    protected virtual WebBrowserSiteBase CreateWebBrowserSiteBase()
    {
        return new WebBrowserSiteBase(this);
    }

    /// <summary>
    ///  This will be called when the native ActiveX control has just been created.
    ///  Inheritors of this class can override this method to cast the nativeActiveXObject
    ///  parameter to the appropriate interface. They can then cache this interface
    ///  value in a member variable. However, they must release this value when
    ///  DetachInterfaces is called (by setting the cached interface variable to null).
    /// </summary>
    protected virtual void AttachInterfaces(object nativeActiveXObject)
    {
    }

    /// <summary>
    ///  See AttachInterfaces for a description of when to override DetachInterfaces.
    /// </summary>
    protected virtual void DetachInterfaces()
    {
    }

    /// <summary>
    ///  This will be called when we are ready to start listening to events.
    ///  Inheritors can override this method to hook their own connection points.
    /// </summary>
    protected virtual void CreateSink()
    {
    }

    /// <summary>
    ///  This will be called when it is time to stop listening to events.
    ///  This is where inheritors have to disconnect their connection points.
    /// </summary>
    protected virtual void DetachSink()
    {
    }

    // DrawToBitmap doesn't work for this control, so we should hide it. We'll
    // still call base so that this has a chance to work if it can.
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds)
    {
        base.DrawToBitmap(bitmap, targetBounds);
    }

    //
    // Overriding methods: Overrides of some of Control's virtual methods.
    //

    //
    // Sets the site of this component. A non-null value indicates that this
    // component has been added to a container, and a null value indicates that
    // this component is being removed from a container.
    //
    public override ISite? Site
    {
        set
        {
            bool hadSelectionHandler = RemoveSelectionHandler();

            base.Site = value;

            if (hadSelectionHandler)
            {
                AddSelectionHandler();
            }
        }
    }

    /// <remarks>
    ///  <para>We have to resize the ActiveX control when our size changes.</para>
    /// </remarks>
    internal override unsafe void OnBoundsUpdate(int x, int y, int width, int height)
    {
        // If the ActiveX control is already InPlaceActive, make sure
        // it's bounds also change.
        if (ActiveXState >= WebBrowserHelper.AXState.InPlaceActive)
        {
            try
            {
                _webBrowserBaseChangingSize.Width = width;
                _webBrowserBaseChangingSize.Height = height;
                RECT posRect = new Rectangle(0, 0, width, height);
                RECT clipRect = WebBrowserHelper.GetClipRect();
                AXInPlaceObject!.SetObjectRects(&posRect, &clipRect);
            }
            finally
            {
                _webBrowserBaseChangingSize.Width = -1;  // Invalid value. Use WebBrowserBase.Bounds instead, when this is the case.
            }
        }

        base.OnBoundsUpdate(x, y, width, height);
    }

    protected override bool ProcessDialogKey(Keys keyData) =>
        !_ignoreDialogKeys && base.ProcessDialogKey(keyData);

    public override unsafe bool PreProcessMessage(ref Message msg)
    {
        // Let us assume that TAB key was pressed. In this case, we should first
        // give a chance to the ActiveX control to see if it wants to change focus
        // to other subitems within it. If we did not give the ActiveX control the
        // first chance to handle the key stroke, and called base.PreProcessMessage,
        // the focus would be changed to the next control on the form! We don't want
        // that

        // If the ActiveX control doesn't want to handle the key, it calls back into
        // WebBrowserSiteBase's IOleControlSite.TranslateAccelerator implementation. There, we
        // set a flag and call back into this method. In this method, we first check
        // if this flag is set. If so, we call base.PreProcessMessage.
        if (!IsUserMode)
        {
            return false;
        }

        if (GetAXHostState(WebBrowserHelper.s_siteProcessedInputKey))
        {
            // In this case, the control called us back through IOleControlSite
            // and is now giving us a chance to see if we want to process it.
            return base.PreProcessMessage(ref msg);
        }

        // Convert Message to MSG
        MSG win32Message = msg.ToMSG();
        SetAXHostState(WebBrowserHelper.s_siteProcessedInputKey, false);
        try
        {
            if (_axOleInPlaceObject is null)
            {
                return false;
            }

            // Give the ActiveX control a chance to process this key by calling
            // IOleInPlaceActiveObject::TranslateAccelerator.
            HRESULT hr = _axOleInPlaceActiveObject!.TranslateAccelerator(&win32Message);
            if (hr == HRESULT.S_OK)
            {
                return true;
            }
            else
            {
                // win32Message may have been modified. Lets copy it back.
                msg.MsgInternal = (MessageId)win32Message.message;
                msg.WParamInternal = win32Message.wParam;
                msg.LParamInternal = win32Message.lParam;
                msg.HWnd = win32Message.hwnd;

                if (hr == HRESULT.S_FALSE)
                {
                    // Same code as in AxHost (ignore dialog keys here).
                    // We have the same problem here.
                    bool ret = false;

                    _ignoreDialogKeys = true;
                    try
                    {
                        ret = base.PreProcessMessage(ref msg);
                    }
                    finally
                    {
                        _ignoreDialogKeys = false;
                    }

                    return ret;
                }
                else if (GetAXHostState(WebBrowserHelper.s_siteProcessedInputKey))
                {
                    return base.PreProcessMessage(ref msg);
                }
                else
                {
                    return false;
                }
            }
        }
        finally
        {
            SetAXHostState(WebBrowserHelper.s_siteProcessedInputKey, false);
        }
    }

    //
    // Process a mnemonic character. This is done by manufacturing a
    // WM_SYSKEYDOWN message and passing it to the ActiveX control.
    //
    // We can't decide just by ourselves whether we can process the
    // mnemonic. We have to ask the ActiveX control for it.
    //
    protected internal override unsafe bool ProcessMnemonic(char charCode)
    {
        if (!CanSelect)
        {
            return false;
        }

        bool processed = false;

        try
        {
            CONTROLINFO controlInfo = new()
            {
                cb = (uint)sizeof(CONTROLINFO)
            };

            if (_axOleControl!.GetControlInfo(&controlInfo).Failed)
            {
                return processed;
            }

            // We don't have a message so we must create one ourselves.
            // The message we are creating is a WM_SYSKEYDOWN with the right alt key setting.
            MSG msg = new()
            {
                hwnd = HWND.Null,
                message = PInvokeCore.WM_SYSKEYDOWN,
                wParam = (WPARAM)char.ToUpper(charCode, CultureInfo.CurrentCulture),
                lParam = 0x20180001,
                time = PInvoke.GetTickCount()
            };

            PInvoke.GetCursorPos(out Point p);
            msg.pt = p;
            if (!PInvoke.IsAccelerator(new HandleRef<HACCEL>(this, controlInfo.hAccel), controlInfo.cAccel, &msg, lpwCmd: null))
            {
                _axOleControl.OnMnemonic(&msg);
                Focus();
                processed = true;
            }
        }
        catch (Exception ex) when (!ex.IsCriticalException())
        {
            Debug.Fail($"error in processMnemonic: {ex}");
        }

        return processed;
    }

    /// <remarks>
    ///  <para>
    ///   Certain messages are forwardef directly to the ActiveX control, others are first processed by the wndproc of
    ///   <see cref="Control"/>.
    ///  </para>
    /// </remarks>
    protected override unsafe void WndProc(ref Message m)
    {
        switch (m.MsgInternal)
        {
            // Things we explicitly ignore and pass to the ActiveX's windproc
            case PInvokeCore.WM_ERASEBKGND:
            case MessageId.WM_REFLECT_NOTIFYFORMAT:
            case PInvokeCore.WM_SETCURSOR:
            case PInvokeCore.WM_SYSCOLORCHANGE:
            case PInvokeCore.WM_LBUTTONDBLCLK:
            case PInvokeCore.WM_LBUTTONUP:
            case PInvokeCore.WM_MBUTTONDBLCLK:
            case PInvokeCore.WM_MBUTTONUP:
            case PInvokeCore.WM_RBUTTONDBLCLK:
            case PInvokeCore.WM_RBUTTONUP:
            case PInvokeCore.WM_CONTEXTMENU:
            //
            // Some of the MSComCtl controls respond to this message to do some
            // custom painting. So, we should just pass this message through.
            case PInvokeCore.WM_DRAWITEM:
                DefWndProc(ref m);
                break;

            case PInvokeCore.WM_COMMAND:
                if (!ReflectMessage(m.LParamInternal, ref m))
                {
                    DefWndProc(ref m);
                }

                break;

            case PInvokeCore.WM_HELP:
                // We want to both fire the event, and let the ActiveX have the message...
                base.WndProc(ref m);
                DefWndProc(ref m);
                break;

            case PInvokeCore.WM_LBUTTONDOWN:
            case PInvokeCore.WM_MBUTTONDOWN:
            case PInvokeCore.WM_RBUTTONDOWN:
            case PInvokeCore.WM_MOUSEACTIVATE:
                if (!DesignMode)
                {
                    if (_containingControl is not null && _containingControl.ActiveControl != this)
                    {
                        Focus();
                    }
                }

                DefWndProc(ref m);
                break;

            case PInvokeCore.WM_DESTROY:
                //
                // If we are currently in a state of InPlaceActive or above,
                // we should first reparent the ActiveX control to our parking
                // window before we transition to a state below InPlaceActive.
                // Otherwise we face all sorts of problems when we try to
                // transition back to a state >= InPlaceActive.
                //
                if (ActiveXState >= WebBrowserHelper.AXState.InPlaceActive)
                {
                    HWND hwndInPlaceObject = HWND.Null;
                    if (AXInPlaceObject!.GetWindow(&hwndInPlaceObject).Succeeded)
                    {
                        Application.ParkHandle(new HandleRef<HWND>(AXInPlaceObject, hwndInPlaceObject), DpiAwarenessContext);
                    }
                }

                if (RecreatingHandle)
                {
                    _axReloadingState = _axState;
                }

                //
                // If the ActiveX control was holding on to our handle we need
                // to get it to throw it away. This, we do by transitioning it
                // down below InPlaceActivate (because it is when transitioning
                // up to InPlaceActivate that the ActiveX control grabs our handle).
                TransitionDownTo(WebBrowserHelper.AXState.Running);

                _axWindow?.ReleaseHandle();

                OnHandleDestroyed(EventArgs.Empty);
                break;

            default:
                if (m.MsgInternal == WebBrowserHelper.REGMSG_MSG)
                {
                    m.ResultInternal = (LRESULT)WebBrowserHelper.REGMSG_RETVAL;
                }
                else
                {
                    base.WndProc(ref m);
                }

                break;
        }
    }

    protected override void OnParentChanged(EventArgs e)
    {
        Control? parent = ParentInternal;
        if ((Visible && parent is not null && parent.Visible) || IsHandleCreated)
        {
            TransitionUpTo(WebBrowserHelper.AXState.InPlaceActive);
        }

        base.OnParentChanged(e);
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        if (Visible && !Disposing && !IsDisposed)
        {
            TransitionUpTo(WebBrowserHelper.AXState.InPlaceActive);
        }

        base.OnVisibleChanged(e);
    }

    protected override void OnGotFocus(EventArgs e)
    {
        if (ActiveXState < WebBrowserHelper.AXState.UIActive)
        {
            TransitionUpTo(WebBrowserHelper.AXState.UIActive);
        }

        base.OnGotFocus(e);
    }

    protected override void OnLostFocus(EventArgs e)
    {
        base.OnLostFocus(e);

        // If the focus goes from our control window to one of the child windows,
        // we should not deactivate.
        if (!ContainsFocus)
        {
            TransitionDownTo(WebBrowserHelper.AXState.InPlaceActive);
        }
    }

    protected override void OnRightToLeftChanged(EventArgs e)
    {
        // Do nothing: no point in recreating the handle when we don't obey RTL
    }

    //
    // Can't select the control until the ActiveX control is InPlaceActive.
    //
    internal override bool CanSelectCore()
    {
        return ActiveXState >= WebBrowserHelper.AXState.InPlaceActive && base.CanSelectCore();
    }

    internal override bool AllowsKeyboardToolTip()
    {
        return false;
    }

    //
    // We have to inform the ActiveX control that an ambient property
    // has changed.
    //
    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        AmbientChanged(PInvokeCore.DISPID_AMBIENT_FONT);
    }

    //
    // We have to inform the ActiveX control that an ambient property
    // has changed.
    //
    protected override void OnForeColorChanged(EventArgs e)
    {
        base.OnForeColorChanged(e);
        AmbientChanged(PInvokeCore.DISPID_AMBIENT_FORECOLOR);
    }

    //
    // We have to inform the ActiveX control that an ambient property
    // has changed.
    //
    protected override void OnBackColorChanged(EventArgs e)
    {
        base.OnBackColorChanged(e);
        AmbientChanged(PInvokeCore.DISPID_AMBIENT_BACKCOLOR);
    }

    //
    // TransitionDownTo Passive when we are being disposed.
    //
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            TransitionDownTo(WebBrowserHelper.AXState.Passive);
        }

        base.Dispose(disposing);
    }

    //
    // Internal helper methods:
    //

    internal WebBrowserHelper.AXState ActiveXState
    {
        get => _axState;
        set => _axState = value;
    }

    internal bool GetAXHostState(int mask) => _axHostState[mask];

    internal void SetAXHostState(int mask, bool value) => _axHostState[mask] = value;

    internal IntPtr GetHandleNoCreate() => IsHandleCreated ? Handle : IntPtr.Zero;

    internal void TransitionUpTo(WebBrowserHelper.AXState state)
    {
        if (GetAXHostState(WebBrowserHelper.s_inTransition))
        {
            return;
        }

        SetAXHostState(WebBrowserHelper.s_inTransition, true);

        try
        {
            while (state > ActiveXState)
            {
                switch (ActiveXState)
                {
                    case WebBrowserHelper.AXState.Passive:
                        TransitionFromPassiveToLoaded();
                        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Loaded, "Failed transition");
                        break;
                    case WebBrowserHelper.AXState.Loaded:
                        TransitionFromLoadedToRunning();
                        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Running, "Failed transition");
                        break;
                    case WebBrowserHelper.AXState.Running:
                        TransitionFromRunningToInPlaceActive();
                        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Failed transition");
                        break;
                    case WebBrowserHelper.AXState.InPlaceActive:
                        TransitionFromInPlaceActiveToUIActive();
                        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.UIActive, "Failed transition");
                        break;
                    default:
                        Debug.Fail("bad state");
                        ActiveXState++; // To exit the loop
                        break;
                }
            }
        }
        finally
        {
            SetAXHostState(WebBrowserHelper.s_inTransition, false);
        }
    }

    internal void TransitionDownTo(WebBrowserHelper.AXState state)
    {
        if (!GetAXHostState(WebBrowserHelper.s_inTransition))
        {
            SetAXHostState(WebBrowserHelper.s_inTransition, true);

            try
            {
                while (state < ActiveXState)
                {
                    switch (ActiveXState)
                    {
                        case WebBrowserHelper.AXState.UIActive:
                            TransitionFromUIActiveToInPlaceActive();
                            Debug.Assert(ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Failed transition");
                            break;
                        case WebBrowserHelper.AXState.InPlaceActive:
                            TransitionFromInPlaceActiveToRunning();
                            Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Running, "Failed transition");
                            break;
                        case WebBrowserHelper.AXState.Running:
                            TransitionFromRunningToLoaded();
                            Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Loaded, "Failed transition");
                            break;
                        case WebBrowserHelper.AXState.Loaded:
                            TransitionFromLoadedToPassive();
                            Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Passive, "Failed transition");
                            break;
                        default:
                            Debug.Fail("bad state");
                            ActiveXState--; // To exit the loop
                            break;
                    }
                }
            }
            finally
            {
                SetAXHostState(WebBrowserHelper.s_inTransition, false);
            }
        }
    }

    internal unsafe bool DoVerb(OLEIVERB verb)
    {
        RECT posRect = Bounds;
        using var clientSite = ComHelpers.GetComScope<IOleClientSite>(ActiveXSite);
        HRESULT hr = _axOleObject!.DoVerb((int)verb, null, clientSite, 0, HWND, &posRect);
        Debug.Assert(hr.Succeeded, $"DoVerb call failed for verb 0x{verb}");
        return hr.Succeeded;
    }

    //
    // Returns this control's logically containing form.
    // At design time this is always the form being designed.
    // At runtime it is the parent form.
    // By default, the parent form performs that function. It is
    // however possible for another form higher in the parent chain
    // to serve in that role. The logical container of this
    // control determines the set of logical sibling control.
    // This property exists only to enable some specific
    // behaviors of ActiveX controls.
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    internal ContainerControl? ContainingControl
    {
        get
        {
            if (_containingControl is null ||
                GetAXHostState(WebBrowserHelper.s_recomputeContainingControl))
            {
                _containingControl = FindContainerControlInternal();
            }

            return _containingControl;
        }
    }

    internal WebBrowserContainer CreateWebBrowserContainer() =>
        _wbContainer ??= new WebBrowserContainer(this);

    internal WebBrowserContainer GetParentContainer()
    {
        _container ??= WebBrowserContainer.FindContainerForControl(this);

        if (_container is null)
        {
            _container = CreateWebBrowserContainer();
            _container.AddControl(this);
        }

        return _container;
    }

    internal void SetEditMode(WebBrowserHelper.AXEditMode em)
    {
        _axEditMode = em;
    }

    internal void SetSelectionStyle(WebBrowserHelper.SelectionStyle selectionStyle)
    {
        if (DesignMode)
        {
            ISelectionService? iss = WebBrowserHelper.GetSelectionService(this);
            _selectionStyle = selectionStyle;
            if (iss is not null && iss.GetComponentSelected(this))
            {
                // The ActiveX Host designer will offer an extender property
                // called "SelectionStyle"
                PropertyDescriptor? prop = TypeDescriptor.GetProperties(this)["SelectionStyle"];
                if (prop is not null && prop.PropertyType == typeof(int))
                {
                    prop.SetValue(this, (int)selectionStyle);
                }
            }
        }
    }

    internal void AddSelectionHandler()
    {
        if (!GetAXHostState(WebBrowserHelper.s_addedSelectionHandler))
        {
            SetAXHostState(WebBrowserHelper.s_addedSelectionHandler, true);

            ISelectionService? iss = WebBrowserHelper.GetSelectionService(this);
            iss?.SelectionChanging += SelectionChangeHandler;
        }
    }

    internal bool RemoveSelectionHandler()
    {
        bool retVal = GetAXHostState(WebBrowserHelper.s_addedSelectionHandler);
        if (retVal)
        {
            SetAXHostState(WebBrowserHelper.s_addedSelectionHandler, false);

            ISelectionService? iss = WebBrowserHelper.GetSelectionService(this);
            iss?.SelectionChanging -= SelectionChangeHandler;
        }

        return retVal;
    }

    internal void AttachWindow(HWND hwnd)
    {
        PInvoke.SetParent(hwnd, this);

        _axWindow?.ReleaseHandle();

        _axWindow = new WebBrowserBaseNativeWindow(this);
        _axWindow.AssignHandle(hwnd, false);

        UpdateZOrder();
        UpdateBounds();

        Size extent = Size;
        extent = SetExtent(extent.Width, extent.Height);

        Point location = Location;
        Bounds = new Rectangle(location.X, location.Y, extent.Width, extent.Height);
    }

    internal bool IsUserMode => Site is null || !DesignMode;

    internal void MakeDirty()
    {
        if (Site.TryGetService(out IComponentChangeService? changeService))
        {
            changeService.OnComponentChanging(this);
            changeService.OnComponentChanged(this);
        }
    }

    internal int NoComponentChangeEvents { get; set; }

    //
    // Private helper methods:
    //

    private void StartEvents()
    {
        if (!GetAXHostState(WebBrowserHelper.s_sinkAttached))
        {
            SetAXHostState(WebBrowserHelper.s_sinkAttached, true);
            CreateSink();
        }

        ActiveXSite.StartEvents();
    }

    private void StopEvents()
    {
        if (GetAXHostState(WebBrowserHelper.s_sinkAttached))
        {
            SetAXHostState(WebBrowserHelper.s_sinkAttached, false);
            DetachSink();
        }

        ActiveXSite.StopEvents();
    }

    private void TransitionFromPassiveToLoaded()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Passive, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.Passive)
        {
            // First, create the ActiveX control
            Debug.Assert(_activeXInstance is null, "activeXInstance must be null");

            HRESULT hr = PInvokeCore.CoCreateInstance(
                in _clsid,
                null,
                CLSCTX.CLSCTX_INPROC_SERVER,
                IID.GetRef<IUnknown>(),
                out void* unknown);

            hr.ThrowOnFailure();

            _activeXInstance = ComHelpers.GetObjectForIUnknown((IUnknown*)unknown);

            Debug.Assert(_activeXInstance is not null, "w/o an exception being thrown we must have an object...");

            // We are now loaded.
            ActiveXState = WebBrowserHelper.AXState.Loaded;

            // Lets give them a chance to cast the ActiveX object to the appropriate interfaces.
            AttachInterfacesInternal();
        }
    }

    private void TransitionFromLoadedToPassive()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Loaded, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.Loaded)
        {
            // Need to make sure that we don't handle any PropertyChanged notifications at this point.
            NoComponentChangeEvents++;
            try
            {
                // Release the activeXInstance
                if (_activeXInstance is not null)
                {
                    // Lets first get the cached interface pointers of activeXInstance released.
                    DetachInterfacesInternal();

                    Marshal.FinalReleaseComObject(_activeXInstance);
                    _activeXInstance = null;
                }
            }
            finally
            {
                NoComponentChangeEvents--;
            }

            // We are now Passive.
            ActiveXState = WebBrowserHelper.AXState.Passive;
        }
    }

    private unsafe void TransitionFromLoadedToRunning()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Loaded, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.Loaded)
        {
            // See if the ActiveX control returns OLEMISC_SETCLIENTSITEFIRST
            HRESULT hr = _axOleObject.GetMiscStatus(DVASPECT.DVASPECT_CONTENT, out OLEMISC bits);
            if (hr.Succeeded && bits.HasFlag(OLEMISC.OLEMISC_SETCLIENTSITEFIRST))
            {
                // Simply setting the site to the ActiveX control should activate it.
                // And this will take us to the Running state.
                _axOleObject!.SetClientSite(ComHelpers.GetComPointer<IOleClientSite>(ActiveXSite));
            }

            // We start receiving events now (but we do this only if we are not in DesignMode).
            if (!DesignMode)
            {
                StartEvents();
            }

            // We are now Running.
            ActiveXState = WebBrowserHelper.AXState.Running;
        }
    }

    private unsafe void TransitionFromRunningToLoaded()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Running, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.Running)
        {
            StopEvents();

            // Remove ourselves from our parent container...
            WebBrowserContainer parentContainer = GetParentContainer();
            parentContainer?.RemoveControl(this);

            // Now inform the ActiveX control that it's been un-sited.
            _axOleObject!.SetClientSite(null);

            // We are now Loaded!
            ActiveXState = WebBrowserHelper.AXState.Loaded;
        }
    }

    private void TransitionFromRunningToInPlaceActive()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.Running, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.Running)
        {
            try
            {
                DoVerb(OLEIVERB.OLEIVERB_INPLACEACTIVATE);
            }
            catch (Exception t)
            {
                throw new TargetInvocationException(string.Format(SR.AXNohWnd, GetType().Name), t);
            }

            CreateControl(true);

            //
            // We are now InPlaceActive!
            ActiveXState = WebBrowserHelper.AXState.InPlaceActive;
        }
    }

    private void TransitionFromInPlaceActiveToRunning()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.InPlaceActive)
        {
            // First, lets make sure we transfer the ContainingControl's ActiveControl
            // before we InPlaceDeactivate.
            ContainerControl? containingControl = ContainingControl;
            if (containingControl is not null && containingControl.ActiveControl == this)
            {
                containingControl.SetActiveControl(null);
            }

            // Now, InPlaceDeactivate.
            AXInPlaceObject!.InPlaceDeactivate();

            // We are now Running!
            ActiveXState = WebBrowserHelper.AXState.Running;
        }
    }

    private void TransitionFromInPlaceActiveToUIActive()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.InPlaceActive)
        {
            try
            {
                DoVerb(OLEIVERB.OLEIVERB_UIACTIVATE);
            }
            catch (Exception t)
            {
                throw new TargetInvocationException(string.Format(SR.AXNohWnd, GetType().Name), t);
            }

            //
            // We are now UIActive
            ActiveXState = WebBrowserHelper.AXState.UIActive;
        }
    }

    private void TransitionFromUIActiveToInPlaceActive()
    {
        Debug.Assert(ActiveXState == WebBrowserHelper.AXState.UIActive, "Wrong start state to transition from");
        if (ActiveXState == WebBrowserHelper.AXState.UIActive)
        {
            HRESULT hr = AXInPlaceObject!.UIDeactivate();
            Debug.Assert(hr.Succeeded, "Failed to UIDeactivate");

            // We are now InPlaceActive
            ActiveXState = WebBrowserHelper.AXState.InPlaceActive;
        }
    }

    internal WebBrowserSiteBase ActiveXSite =>
        _axSite ??= CreateWebBrowserSiteBase();

    private void AttachInterfacesInternal()
    {
        Debug.Assert(_activeXInstance is not null, "The native control is null");
        _axOleObject = (IOleObject.Interface)_activeXInstance;
        _axOleInPlaceObject = (IOleInPlaceObject.Interface)_activeXInstance;
        _axOleInPlaceActiveObject = (IOleInPlaceActiveObject.Interface)_activeXInstance;
        _axOleControl = (IOleControl.Interface)_activeXInstance;

        // Give the inheriting classes a chance to cast the ActiveX object to the
        // appropriate interfaces.
        AttachInterfaces(_activeXInstance);
    }

    private void DetachInterfacesInternal()
    {
        _axOleObject = null;
        _axOleInPlaceObject = null;
        _axOleInPlaceActiveObject = null;
        _axOleControl = null;

        // Lets give the inheriting classes a chance to release
        // their cached interfaces of the ActiveX object.
        DetachInterfaces();
    }

    // We need to change the ActiveX control's state when selection changes.
    private EventHandler SelectionChangeHandler => _selectionChangeHandler ??= OnNewSelection;

    // We need to do special stuff (convert window messages to interface calls)
    // during design time when selection changes.
    private void OnNewSelection(object? sender, EventArgs e)
    {
        if (DesignMode)
        {
            ISelectionService? iss = WebBrowserHelper.GetSelectionService(this);
            if (iss is not null)
            {
                // We are no longer selected.
                if (!iss.GetComponentSelected(this))
                {
                    // We need to exit editmode if we were in one.
                    if (EditMode)
                    {
                        GetParentContainer().OnExitEditMode(this);
                        SetEditMode(WebBrowserHelper.AXEditMode.None);
                    }

                    SetSelectionStyle(WebBrowserHelper.SelectionStyle.Selected);
                    RemoveSelectionHandler();
                }
                else
                {
                    //
                    // The AX Host designer will offer an extender property called "SelectionStyle"
                    PropertyDescriptor? prop = TypeDescriptor.GetProperties(this)["SelectionStyle"];
                    if (prop is not null && prop.PropertyType == typeof(int))
                    {
                        int curSelectionStyle = (int)prop.GetValue(this)!;
                        if (curSelectionStyle != (int)_selectionStyle)
                        {
                            prop.SetValue(this, _selectionStyle);
                        }
                    }
                }
            }
        }
    }

    private unsafe Size SetExtent(int width, int height)
    {
        Size sz = new(width, height);
        bool resetExtents = DesignMode;
        Pixel2hiMetric(ref sz);
        HRESULT hr = _axOleObject!.SetExtent(DVASPECT.DVASPECT_CONTENT, (SIZE*)&sz);
        if (hr != HRESULT.S_OK)
        {
            resetExtents = true;
        }

        if (resetExtents)
        {
            _axOleObject.GetExtent(DVASPECT.DVASPECT_CONTENT, (SIZE*)&sz);
            _axOleObject.SetExtent(DVASPECT.DVASPECT_CONTENT, (SIZE*)&sz);
        }

        return GetExtent();
    }

    private unsafe Size GetExtent()
    {
        Size size = default;
        _axOleObject!.GetExtent(DVASPECT.DVASPECT_CONTENT, (SIZE*)&size);
        HiMetric2Pixel(ref size);
        return size;
    }

    private unsafe void HiMetric2Pixel(ref Size sz)
    {
        Point phm = new(sz.Width, sz.Height);
        PointF pcont = default;
        ((IOleControlSite.Interface)ActiveXSite).TransformCoords(
            (POINTL*)&phm,
            &pcont,
            (uint)(XFORMCOORDS.XFORMCOORDS_SIZE | XFORMCOORDS.XFORMCOORDS_HIMETRICTOCONTAINER));
        sz.Width = (int)pcont.X;
        sz.Height = (int)pcont.Y;
    }

    private unsafe void Pixel2hiMetric(ref Size sz)
    {
        Point phm = default;
        PointF pcont = new(sz.Width, sz.Height);
        ((IOleControlSite.Interface)ActiveXSite).TransformCoords(
            (POINTL*)&phm,
            &pcont,
            (uint)(XFORMCOORDS.XFORMCOORDS_SIZE | XFORMCOORDS.XFORMCOORDS_CONTAINERTOHIMETRIC));
        sz.Width = phm.X;
        sz.Height = phm.Y;
    }

    private bool EditMode =>
        _axEditMode != WebBrowserHelper.AXEditMode.None;

    // Find the uppermost ContainerControl that this control lives in
    internal ContainerControl? FindContainerControlInternal()
    {
        if (Site.TryGetService(out IDesignerHost? host) && host.RootComponent is ContainerControl rootContainerControl)
        {
            return rootContainerControl;
        }

        ContainerControl? containerControl = null;
        for (Control? control = this; control is not null; control = control.ParentInternal)
        {
            if (control is ContainerControl tempCC)
            {
                containerControl = tempCC;
            }
        }

        if (containerControl is null && IsHandleCreated)
        {
            containerControl = FromHandle(PInvoke.GetParent(this)) as ContainerControl;
        }

        // Never use the parking window for this: its hwnd can be destroyed at any time.
        if (containerControl is Application.ParkingWindow)
        {
            containerControl = null;
        }

        SetAXHostState(WebBrowserHelper.s_recomputeContainingControl, containerControl is null);

        return containerControl;
    }

    private void AmbientChanged(int dispid)
    {
        if (_activeXInstance is not null)
        {
            Invalidate();
            HRESULT result = _axOleControl!.OnAmbientPropertyChange(dispid);
            Debug.Assert(!result.Failed, $"{result}");
        }
    }

    internal IOleInPlaceObject.Interface? AXInPlaceObject => _axOleInPlaceObject;

    // ---------------------------------------------------------------
    // The following properties implemented in the Control class don't make
    // sense for ActiveX controls. So we block them here.
    // ---------------------------------------------------------------

    //
    // Overridden properties:
    //

    protected override Size DefaultSize => new(75, 23);

    //
    // Overridden methods:
    //

    protected override bool IsInputChar(char charCode)
    {
        return true;
    }

    /// <summary>
    ///  Inheriting classes should override this method to find out when the
    ///  handle has been created. Call base.OnHandleCreated first.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void OnHandleCreated(EventArgs e)
    {
        //
        // This is needed to prevent some controls (for e.g. Office Web Components) from
        // failing to InPlaceActivate() when they call RegisterDragDrop() but do not call
        // OleInitialize(). The EE calls CoInitializeEx() on the thread, but I believe
        // that is not good enough for DragDrop.
        //
        if (Application.OleRequired() != ApartmentState.STA)
        {
            throw new ThreadStateException(SR.ThreadMustBeSTA);
        }

        base.OnHandleCreated(e);

        // make sure we restore whatever running state we had prior to the handle recreate.
        //
        if (_axReloadingState != WebBrowserHelper.AXState.Passive && _axReloadingState != _axState)
        {
            if (_axState < _axReloadingState)
            {
                TransitionUpTo(_axReloadingState);
            }
            else
            {
                TransitionDownTo(_axReloadingState);
            }

            _axReloadingState = WebBrowserHelper.AXState.Passive;
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override Color BackColor
    {
        get => base.BackColor;
        set => base.BackColor = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [AllowNull]
    public override Font Font
    {
        get => base.Font;
        set => base.Font = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override Color ForeColor
    {
        get => base.ForeColor;
        set => base.ForeColor = value;
    }

    /// <summary>
    ///  Hide ImeMode: it doesn't make sense for this control
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new ImeMode ImeMode
    {
        get => base.ImeMode;
        set => base.ImeMode = value;
    }

    //
    // Properties blocked at design time and run time:
    //
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override bool AllowDrop
    {
        get => base.AllowDrop;
        set
        {
            throw new NotSupportedException(SR.WebBrowserAllowDropNotSupported);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override Image? BackgroundImage
    {
        get => base.BackgroundImage;
        set
        {
            throw new NotSupportedException(SR.WebBrowserBackgroundImageNotSupported);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override ImageLayout BackgroundImageLayout
    {
        get => base.BackgroundImageLayout;
        set
        {
            throw new NotSupportedException(SR.WebBrowserBackgroundImageLayoutNotSupported);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [AllowNull]
    public override Cursor Cursor
    {
        get => base.Cursor;
        set
        {
            throw new NotSupportedException(SR.WebBrowserCursorNotSupported);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new bool Enabled
    {
        get => base.Enabled;
        set
        {
            throw new NotSupportedException(SR.WebBrowserEnabledNotSupported);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Localizable(false)]
    public override RightToLeft RightToLeft
    {
        get => RightToLeft.No;
        set
        {
            throw new NotSupportedException(SR.WebBrowserRightToLeftNotSupported);
        }
    }

    // Override this property so that the Bindable attribute can be set to false.
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Bindable(false)]
    [AllowNull]
    public override string Text
    {
        get => string.Empty;
        set => throw new NotSupportedException(SR.WebBrowserTextNotSupported);
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new bool UseWaitCursor
    {
        get => base.UseWaitCursor;
        set
        {
            throw new NotSupportedException(SR.WebBrowserUseWaitCursorNotSupported);
        }
    }

    //
    // Unavailable events
    //

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? BackgroundImageLayoutChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "BackgroundImageLayoutChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? Enter
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "Enter"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? Leave
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "Leave"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? MouseCaptureChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseCaptureChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseClick
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseClick"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseDoubleClick
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseDoubleClick"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? BackColorChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "BackColorChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? BackgroundImageChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "BackgroundImageChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? BindingContextChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "BindingContextChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? CursorChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "CursorChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? EnabledChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "EnabledChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? FontChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "FontChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? ForeColorChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "ForeColorChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? RightToLeftChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "RightToLeftChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? TextChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "TextChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? Click
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "Click"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event DragEventHandler? DragDrop
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "DragDrop"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event DragEventHandler? DragEnter
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "DragEnter"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event DragEventHandler? DragOver
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "DragOver"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? DragLeave
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "DragLeave"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event GiveFeedbackEventHandler? GiveFeedback
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "GiveFeedback"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    // Everett
    public new event HelpEventHandler? HelpRequested
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "HelpRequested"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event PaintEventHandler? Paint
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "Paint"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event QueryContinueDragEventHandler? QueryContinueDrag
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "QueryContinueDrag"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event QueryAccessibilityHelpEventHandler? QueryAccessibilityHelp
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "QueryAccessibilityHelp"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? DoubleClick
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "DoubleClick"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? ImeModeChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "ImeModeChanged"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event KeyEventHandler? KeyDown
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "KeyDown"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event KeyPressEventHandler? KeyPress
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "KeyPress"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event KeyEventHandler? KeyUp
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "KeyUp"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event LayoutEventHandler? Layout
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "Layout"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseDown
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseDown"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? MouseEnter
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseEnter"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? MouseLeave
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseLeave"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? MouseHover
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseHover"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseMove
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseMove"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseUp
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseUp"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event MouseEventHandler? MouseWheel
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "MouseWheel"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event UICuesEventHandler? ChangeUICues
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "ChangeUICues"));
        remove { }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? StyleChanged
    {
        add => throw new NotSupportedException(string.Format(SR.AXAddInvalidEvent, "StyleChanged"));
        remove { }
    }
}
